WPF / Prism库和多个shell

时间:2011-05-10 18:29:27

标签: wpf prism

我对Prism很新,在玩了一下之后,出现了一些问题。我正在尝试创建一个基本上包含shell窗口中的地图控件的模块化应用程序。插件模块提供了与地图交互的不同工具。有些模块非常独立,只是在地图上显示引脚。

  • 第一个问题:RegionManager如何为必须与主地图控件交互的特定于模块的类(演示者)发挥作用?通常在RegionManager中注册一个链接到ViewModel的特定视图,但在我的情况下,有一个单独的视图(地图视图),多个演示者对其进行操作。

  • 第二个问题:我需要能够打开几个窗口(shell) - 有点像MS Word文档 - 应该由插件模块进行扩展。在单shell环境中,当实例化模块特定类时,它们可以使用依赖注入容器来获取对RegionManager或Shell本身的引用,以便访问映射控件。但是有了多个shell,我看不到如何访问右shell的map控件。依赖项容器引用了对应用程序的全局对象,而不是我当前正在使用的shell的特定内容。对于EventAggregator也是如此。

非常欢迎任何意见,

2 个答案:

答案 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());
    }
}

第二个问题可以通过许多不同的方式解决:

  • Application.Current.Windows
  • 包含ShellViewModel列表的全局MainViewModel,如果添加新视图模型,它将显示在新窗口中。所有窗口的引导程序都是单一的。
  • 传递给引导程序构造函数的某种共享状态。

我不知道这些窗口是如何相互关联的,我不知道哪种方式最好,也许可以编写一个带有独立窗口的应用程序。