我对Prism很新,在玩了一下之后,出现了一些问题。我正在尝试创建一个基本上包含shell窗口中的地图控件的模块化应用程序。插件模块提供了与地图交互的不同工具。有些模块非常独立,只是在地图上显示引脚。
第一个问题:RegionManager如何为必须与主地图控件交互的特定于模块的类(演示者)发挥作用?通常在RegionManager中注册一个链接到ViewModel的特定视图,但在我的情况下,有一个单独的视图(地图视图),多个演示者对其进行操作。
第二个问题:我需要能够打开几个窗口(shell) - 有点像MS Word文档 - 应该由插件模块进行扩展。在单shell环境中,当实例化模块特定类时,它们可以使用依赖注入容器来获取对RegionManager或Shell本身的引用,以便访问映射控件。但是有了多个shell,我看不到如何访问右shell的map控件。依赖项容器引用了对应用程序的全局对象,而不是我当前正在使用的shell的特定内容。对于EventAggregator也是如此。
非常欢迎任何意见,
版
答案 0 :(得分:2)
在阅读与Prism相关的文章和论坛数小时之后,我在Erwin van der Valk的博客上看到了“如何构建Outlook风格应用程序”一文 - How to Build an Outlook Style Application。
在架构的一部分中,Unity子容器用于解析类型实例。这正是我对第二个问题的答案所需要的:我需要“作用域”(通过窗口)依赖注入(例如:窗口范围的EventAggregator,Map控件等)
以下是我创建新窗口的方法:
private IShellWindow CreateNewShell(IRegionManager regionManager)
{
IUnityContainer childContainer = this.Container.CreateChildContainer();
... register types in child container ...
var window = new ShellWindow();
RegionManager.SetRegionManager(window, regionManager);
window.Content = childContainer.Resolve<MapDocumentView>();
return window;
}
因此MapDocumentView及其所有组件将被注入(如果需要)窗口范围的实例。
既然我可以使用scoped注入的对象,我可以在基于模块的MapPresenter中获取窗口范围的地图。为了回答我的第一个问题,我定义了一个接口IHostApplication,它由具有MapPresenterRegistry属性的Bootstrapper实现。此接口将添加到主容器中 初始化后,模块将注册其演示者,并在创建窗口时,将实例化它们。
所以对于模块初始化:
public void Initialize()
{
...
this.hostApplication.MapPresenterRegistry.Add(typeof(ModuleSpecificMapPresenter));
...
}
初始化地图窗口的代码:
private void View_Loaded(object sender, RoutedEventArgs e)
{
// Register map in the == scoped container ==
container.RegisterInstance<IMap>(this.View.Map);
// Create map presenters
var hostApplication = this.container.Resolve<IHostApplication>();
foreach (var mapPresenterType in hostApplication.MapPresenterRegistry)
{
var mapPresenter = this.container.Resolve(mapPresenterType) as IMapPresenter;
if (mapPresenter != null)
{
this.mapPresenters.Add(mapPresenter);
}
}
}
特定于模块的MapPresenter:
public ModuleSpecificMapPresenter(IEventAggregator eventAggregator, IMap map)
{
this.eventAggregator = eventAggregator;
this.map = map;
this.eventAggregator.GetEvent<AWindowSpecificEvent>().Subscribe(this.WindowSpecificEventFired);
// Do stuff on with the map
}
所以这些是我解决方案的重点。我不喜欢的是我没有以这种方式利用区域管理。我几乎有自定义代码来完成这项工作。
如果您有任何进一步的想法,我会很高兴听到他们的意见。 爱德华
答案 1 :(得分:1)
您有一个主视图和多个子视图,并且可以通过不同的模块添加子视图。
我不确定在这种情况下是否可以应用RegionManager
类,因此我将创建一个单独的全局类IPinsCollectionState
,它必须在引导程序中注册为单例。
public interface IPin
{
Point Coordinates { get; }
IPinView View { get; }
//You can use a view model or a data template instead of the view interface, but this example is the simplest
}
public interface IPinsCollectionState
{
ObservableCollection<IPin> Pins { get; }
}
您的主视图模型和不同模块可以将此接口作为构造函数参数接收:
public class MapViewModel
{
public MapViewModel(IPinsCollectionState collectionState)
{
foreach (var item in collectionState.Pins)
{ /* Do something */ };
collectionState.Pins.CollectionChanged += (s, e) => {/* Handle added or removed items in the future */};
}
//...
}
模块视图模型示例:
public class Module1ViewModel
{
public Module1ViewModel(IPinsCollectionState collectionState)
{
//somewhere in the code
collectionState.Pins.Add(new Module1Pin());
}
}
第二个问题可以通过许多不同的方式解决:
我不知道这些窗口是如何相互关联的,我不知道哪种方式最好,也许可以编写一个带有独立窗口的应用程序。