2011-05-10 112 views
2

我对Prism很有新意,玩过一段时间后出现了一些问题。我试图创建一个基本上包含在shell窗口中的地图控件的模块化应用程序。插件模块提供了与地图交互的不同工具。有些模块相当独立,只需在地图上显示引脚。WPF/Prism库和多个shell

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

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

任何投入将是非常欢迎的,

埃德

+0

每个引脚都有独立的模块?不,男人,这不酷。模块必须根据其功能而有所不同,但在这种特殊情况下,最好使用包含子视图模型集合的视图模型。接下来,第二个问题提供一些信息,显示在自定义窗口中以及它们如何打开。我认为有必要将引用存储在全局对象中,但我不知道所有的细节,所以我现在还不能确切地说。 – vorrtex 2011-05-10 21:16:27

+0

不,不是每个引脚的模块:)模块应该注册一个类(演示者),它将以某种方式被实例化并赋予Shell的地图控件。然后它可以例如显示城市中所有摄像头的图标。另一个模块可以使用相同的地图来显示交通信息。这就是为什么这些模块是独特的,但应该与相同的“文档 - 全球”地图控制交互。 – Eduard 2011-05-10 22:29:44

+0

自定义窗口与打开新的Word文档基本相同,但在我的情况下,它将是一个“地图文档”。基本上是初始shell的精确副本。因此,在一个窗口中,您可以启用流量,并在另一个窗口中启用相机信息(例如)。关于“他们是如何被打开的”,我正在考虑沿着“文件 - >新文档”的方式。 – Eduard 2011-05-10 22:33:47

回答

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,如果添加新的视图模型,它将显示在新窗口中。引导程序对于所有窗口都是单独的。
  • 传递给引导程序构造函数的某种共享状态。

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

+0

如果我理解正确,我不会使用AutoRegisterBehavior注册来自不同模块的视图。我基本上必须在创建新窗口时“手工创建”MapViewModel(s)。是否有意义?我会发布一个我已经尝试过的解决方案,似乎可以达到我想要的效果。告诉我,如果它是有道理的;) – Eduard 2011-05-11 15:02:58

+0

@Eduard这是什么意思“手动”?如果你选择第二个选项,你可以使用'容器。Resolve'方法,但是您必须将结果存储在全局集合的某处。 – vorrtex 2011-05-11 15:17:31

+0

@vorrtex我发布了我的解决方案。 “手工”是我不使用区域管理框架来创建实例...没有AutoPopulateRegionBehavior。所以基本上我没有使用Prism的那部分。此外,由于我的作用域统一容器,我不确定如何使用RegionManager.RegisterViewWithRegion()开箱即用。 – Eduard 2011-05-11 17:00:26

2

经过几个小时的阅读棱镜相关的文章和论坛我碰到了Erwin van der Valk的博客 - How to Build an Outlook Style Application的文章“如何构建一个Outlook风格的应用程序”。

在架构的一部分中,使用了一个Unity子容器来解析类型实例。这正是我第二个问题的答案所需要的:我需要“范围”(通过窗口)依赖注入(例如:窗口范围的EventAggregator,地图控件等)

以下是我如何创建一个新窗口:

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及其所有组件都将被注入(如果需要)窗口范围的实例。

既然我可以有范围的注入对象,我可以在基于模块的MapPresenter中获取窗口范围的地图。为了回答我的第一个问题,我定义了一个由具有MapPresenterRegistry属性的Bootstrapper实现的接口IHostApplication。该界面被添加到主容器。
初始化后,模块将注册他们的演示者,并在创建窗口时,它们将被实例化。

所以对于模块初始化:

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 
} 

因此,这些都是大我的解决方案的线。我不太喜欢的是我没有这样利用区域管理。我几乎有自定义代码来完成这项工作。

如果您有任何进一步的想法,我会很高兴听到他们。 Eduard