65.9K
CodeProject 正在变化。 阅读更多。
Home

从 Prism 开始 - 第 n 部分的第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (8投票s)

2012 年 8 月 14 日

CPOL

10分钟阅读

viewsIcon

31303

downloadIcon

1428

一些更有趣的概念,如模块和视图。

背景

继续我的 Prism 系列 1,在本系列中,我将讨论一些更有趣的概念,如模块和视图。

模块

在本文中,我们将讨论如何将其他视图/逻辑分解成称为模块的小块,并在 Prism 应用程序中使用它们。我们将从讨论什么是模块开始,然后介绍模块注册、模块加载,最后讨论如何初始化模块。

什么是模块?

您可以将模块视为 Prism 应用程序的构建块。它是一个包含应用程序所需的所有功能和资源的包。Prism 支持在应用程序中进行运行时模块管理,其中每个模块都可以独立开发和测试。Prism 应用程序会在需要时加载模块。在继续之前,让我们看看在模块概念出现之前,我们的应用程序在 Visual Studio 中的架构。

在上图中,我们有一个解决方案,其中包含一个项目,在该项目中有每一个视图、所有服务和所有业务逻辑。我的意思是,一个项目包含了我项目所需的一切。现在,我们想确定如何将我们的应用程序分解成更小的块,即模块,如下图所示。

现在,如果您查看我们的解决方案,您会发现我们仍然有带有 Shell 和 Region 的主项目,而所有主要功能都定义在主项目之外。从上图可以看出,每个模块都是相互独立的。这里每个模块都可以是单个类库、DLL 或 XAP,其中包含一组相关的其他组件。

每个模块都有一个实现 IModule 接口的中心类,这就是 Prism 库标识库的方式。这个接口有一个名为 Initialize 的方法,它负责初始化模块并将其集成到您的 Prism 应用程序中,之后我们就可以与这个模块进行通信。

创建模块

为了创建模块,需要将以下引用添加到项目中

  • Microsoft.Practices.Prism.dll

接下来,我们需要添加一个类库并继承 IModule 接口,如下所示:

public class MyModule : IModule 
{ 
   public void Initialize()
   { // TO DO: } 
}

注册和发现模块

所有模块都需要在运行时加载到特定的应用程序中。ModuleCatalog 包含有关所有要加载的模块的信息,它知道它们的位置和加载顺序。它甚至知道某个模块依赖于另一个模块。我希望有一点是清楚的,我们需要通过代码、XAML 或配置文件将我们的模块注册到 ModuleCatalog。

加载模块

好了,模块生命周期中的下一个过程是加载模块。包含模块的所有程序集都需要加载到内存中。可以从磁盘、目录或 Web 链接(仅限 SL)加载。Prism 允许我们控制何时加载模块。它们可以尽快加载,这称为“可用时加载”,或者可以在应用程序需要它们时加载,这称为“按需加载”。

加载模块的指南

提前决定何时加载模块总是有益的。永远不要忘记考虑以下几点:

  • 应用程序是否需要运行 - 如果需要,那么它必须与应用程序一起下载,并在应用程序加载时初始化。
  • 始终使用 - 如果是这样,它可以在可用时在后台下载。
  • 很少使用 - 如果是这种情况,那么它可以在后台下载,并在需要时初始化。

初始化模块

模块生命周期的最后一个阶段是初始化模块。IModule.Initialize() 为我们做这件事。在这个方法中,我们编写代码来注册我们的类型,我们甚至可以订阅服务或事件。我们甚至可以处理共享服务。我们还可以将我们的视图组合到 Shell 中。所以,基本上 Initialize() 方法就是您可以放入所有代码的地方,这些代码可以使您的模块为在 Prism 应用程序中使用做好准备。

视图

大多数应用程序都需要某种用户可以交互的界面。在这里,我们将讨论视图,它将为用户提供界面,我们还将看看如何将视图添加到 Prism 应用程序。我们将涉及什么是视图、视图组合、视图发现和视图注入。

什么是 View?

通俗地说,视图就是用户与应用程序交互的界面。但在 Prism 应用程序中,这个概念有所不同。在 Prism 中,视图只是用户界面的一个部分。它是用户界面的一個較小的單元,它封装了一部分功能,并与其他用户界面部分耦合。让我们以 Outlook 为例。在 Outlook 中,顶部有工具栏,左侧有导航栏。如果您仔细观察,您会发现这两部分没有任何依赖关系。例如,Outlook 工具栏可能有自己的视图,并且不依赖于应用程序的其他部分。其主要功能是发送消息以及需要执行的操作。导航区域、电子邮件列表、内容区域甚至状态栏也是如此。所以,我们可以看到 Outlook 由多个视图组成。

复合视图

在 Prism 中,一个视图可以由多个视图组成,这也称为复合视图。这里复合视图可以看作是父级,它有子视图,子视图又有子视图,依此类推。视图可以由用户控件、页面、数据模板等组成。基本上,它可以是任何可以用于向用户显示信息的内容。我们还可以拥有多个视图实例。例如,一个选项卡控件,其中有多个显示相同视图的选项卡。

请记住,在创建视图时,不需要设计模式。Prism 库没有特定的要求您使用设计模式来绑定视图。但是,可以随时使用任何可用的设计模式来创建视图。

实现视图的示例代码

让我们通过添加一个用户控件 MenuBarView 来创建一个视图:

<UserControl>
  <Button>MenuBar</Button>
<UserControl>

请注意,这里我没有使用任何设计模式(如 MVC、MVP)来创建我的视图。接下来,我们需要将我们的视图注册到容器。为此,您需要打开您的模块类。在 Initialize() 方法中,我们需要将我们的视图注册到我们的容器。当然,为了做到这一点,我们需要一个容器的引用。

很可能,这又很简单。让我们开始添加 Microsoft.Practices.Unity.dll 的引用。然后我们需要为我们的模块类添加一个构造函数,如下所示:

public class MyModule : IModule
{
   IUnityContainer _container; 
   public MyModule(IUnityContainer container)
    { _container = container ;}
   public void Initialize()
    { _container.RegisterType<MenuBarView>(); }
}

那么,上面代码发生了什么?这里,每当创建模块时,Prism 都会识别一个容器,这里是 Unity 容器。一旦识别出容器,就会调用我们的 Initialize() 方法,我们开始将我们的类型注册到容器。一旦我们的类型被注册到容器,我们就可以开始在模块中使用它们了。

组合视图

那么,下一步是组合我们的视图。在这里,我将使用 MVVM。我们大多数人已经知道,在 MVVM 中,我们从创建接口开始。这里我也创建了名为 IViewIViewModel 的公共接口。现在,

让我们通过添加一个用户控件 MenuBarView 来创建一个视图:

public interface IView
{
  IViewModel ViewModel {get; set;}
}

public interface IViewModel
{
  IView View {get; set;}
}

请记住,在 MVVM 中,ViewModel 永远不能直接引用 View,这就是我创建 IView 的原因。现在为我们的 View 和 ViewModel 添加另一个名为 IMenuBarViewIMenuBarViewModel 的接口,如下所示:

public interface IMenuBarView: IView { }

现在,转到 View (MenuBarView) 的代码隐藏文件,实现接口 IMenuBarView,如下所示:

public partial class MenuBarView: UserControl, IMenuBarView
{ 
  public MenuBarView()
  { 
   InitializeComponent();
  }
  public IViewModel ViewModel
  {
   get{ return (IMenuBarViewModel)DataContext;}
   set{ DataContext = value; }
  }
}

接下来,我们需要做的是,转到 IMenuBarViewModel 并实现 IViewModel,如下所示:

public interface IMenuBarViewModel: IViewModel
{
  public MenuBarView()
  { 
   InitializeComponent();
  }
  public IViewModel ViewModel
  { 
   get{ return (IMenuBarViewModel)DataContext;}
   set{  DataContext = value; }
  }
}

现在,我们将创建一个名为 MenuBarViewModel 的 ViewModel,如下所示:

public class MenuBarViewModel: IMenuBarViewModel
{
 public MenuBarViewModel( IMenuBarView view)
 {
  View = view; 
  View.ViewModel = this;
 }
 public IView View 
 {get; set;}
}

我们还没有完成。首先,我们需要在模块类中将所有新类型注册到容器,如下所示:

public class MyModule : IModule
{
 IUnityContainer _container;
 public MyModule(IUnityContainer container)
 {
   _container = container ;
 }
 public void Initialize()
 {
  _container.RegisterType<IMenuBarView, MenuBarView>();
 }
}

上面的代码段的作用是,每当我请求 IMenuBarView 类型时,我都会得到 MenuBarView 的实例,我们也必须为 IMenuBarViewModel 做同样的事情,如下所示:

public class MyModule : IModule
{
 IUnityContainer _container;
 public MyModule(IunityContainer container)
 {
   _container = container ;
 }
 public void Initialize()
 {
  _container.RegisterType<IMenuBarView, MenuBarView>(); 
  _container.RegisterType<IMenuBarViewModel, MenuBarViewModel>();
 }
}

现在,如果您运行应用程序,您仍然会得到一个空的 Shell。这是因为我们还没有组合我们的视图。

视图组合

视图组合是构建视图的过程。视图由许多视觉元素组成。当这些元素被创建时,视图将在 Shell 创建的区域中显示。这些项目可以通过自动视图发现或通过视图注入来显示。

视图发现

通过视图发现,视图会自动添加到区域。要启用视图发现,我们需要在区域-视图注册表中设置区域名称和视图类型之间的关系。为此,只需调用 RegionManager.RegisterViewWithRegion(name, type) 即可。通常,这在模块初始化或用户执行操作时完成。当区域被创建时,它会查找与区域相关联的所有视图类型,并自动实例化这些视图。

这种行为的一个副作用是,我们无法明确控制何时加载和显示区域内的视图。

视图发现的示例代码

让我们打开 MyModule 类并添加以下代码:

public class MyModule : IModule
{
 IUnityContainer _container;
 IRegionManager _manager;
 public MyModule(IUnityContainer container, IRegionManager manager)
 {
  _container = container ;
  _manager = manager;
 }
 public void Initialize()
 { 
   _container.RegisterType<IMenuBarView, MenuBarView>();
   _container.RegisterType<IMenuBarViewModel, MenuBarViewModel>();
   _manager.RegisterViewWithRegion(RegionNames.MenuBarRegion, typeof(MenuBarView));}
 }

现在,每当创建区域时,区域将自动初始化并实例化我们的 MenuBar 项。此时,如果您运行它,您将在 Shell 中看到您的 MenuBarView

在这里,我们对 MenuBar 如何以及何时初始化和实例化拥有非常有限的控制权。

视图注入

通过视图注入,视图以编程方式添加到区域。这也可以在模块初始化时或作为用户交互的结果来完成。可以通过几种不同的方式实现视图注入。这可以通过调用 RegionManager.Region[“Name”].Add(view, name) 来完成,或者我们可以从 RegionManager 获取 IRegion 的实例,然后直接与区域一起使用,例如 IRegion.Add(view, name)

请记住,在以编程方式添加视图时,需要激活/停用视图。例如,如果您的内容控件有一个区域,该区域已包含一个视图,并且您向其中添加了一个新视图,则需要停用当前视图才能显示新注入的视图。

因此,正如我们所见,视图注入使我们能够更好地控制要加载和显示的视图。我们还可以从区域中删除视图。

在使用视图注入时需要注意的一点是,您不能将视图添加到尚未创建的区域。对于视图发现,当区域创建时,视图会自动创建。因此,在尝试将视图注入区域之前,应该知道已创建了哪些区域。

视图注入的示例代码

让我们再次打开 MyModule 类并添加以下代码:

public class MyModule : IModule
{
  IUnityContainer _container;
  IRegionManager _manager;
  public MyModule(IUnityContainer container, IRegionManager manager)
  {
   _container = container ;
   _manager = manager;
  }
  public void Initialize()
  { 
    _container.RegisterType<IMenuBarView, MenuBarView>(); 
    _container.RegisterType<IMenuBarViewModel, MenuBarViewModel>();

     var viewModel = _container.Resolve<IMenuBarViewModel>(); 
    _manager.Regions[RegionNames.MenuBarRegion].Add(viewModel.View);
 }
}

现在,运行应用程序,您将看到您的 MenuBar 视图。有时,我们需要对区域有更多的控制,在这种情况下,我们可以使用 IRegion 获取区域的实例,如下所示:

public class MyModule : IModule
{
  ...
  ...
  public void Initialize()
  {
   ... 
   ...
   var viewModel = _container.Resolve<IMenuBarViewModel>();
   IRegion region = _manager.Regions[RegionNames.MenuBarRegion];
   region.Add(viewModel.View);
   }
}

现在运行应用程序,您将获得预期的输出。

希望您喜欢阅读这篇文章。

敬请期待我的下一篇文章,即将发布!!!

© . All rights reserved.