在本实验中,你可以学到Prism的基础概念并应用到创建基于 Prism 类库的解决方案中,你可以以此作为创建一个WPF应用程序的起点。完成此实验之后,你将会学习到:
- 创建一个基于Prism类库的解决方案
- 创建和加载一个模块
- 创建一个视图并在外观窗口中显示
准备工作
本主题要求Prism 类库和Unity Application Block (Unity) 程序集:
- Microsoft.Practices.Composite.dll
- Microsoft.Practices.Composite.Presentation.dll
- Microsoft.Practices.Composite.UnityExtensions.dll
- Microsoft.Practices.ServiceLocation.dll
- Microsoft.Practices.Unity.dll
Prism是作为源码发布的,你需要把它编译成Prism类库程序集 (Microsoft.Practices.Composite.dll, Microsoft.Practices.Composite.Presentation.dll, 和Microsoft.Practices.Composite.UnityExtensions.dll)
注意: |
本实验使用的是Unity 容器Container,但你也可以使用Managed Extensibility Framework (MEF)和Prism类库。 |
编译解决方案
- 在Windows资源管理器中,双击下列批处理文件在Visual Studio中打开 Prism 类库解决方案:Desktop & Silverlight - Open Composite Application Library.bat
- 生成解决方案。 Prism 类库程序集将被放到下列文件夹中 CAL\Desktop\<Project>\bin\Debug.
实验步骤Procedures
本实验包括以下任务:
- 任务1: 用Prism 类库创建一个解决方案
- 任务2:添加一个模块
- 任务3:添加一个视图
以下章节详述上述任务。
注意: |
本实验基于HelloWorld解决方案,可以通过运行文件Desktop only - Open QS - Hello World QuickStart.bat来打开它。 |
任务1:用Prism 类库创建一个解决方案
本任务中将带你窗机一个基于Prism类库的解决方案,作为一个开发WPF应用程序的起点。本解决方案包括推荐的Prism基础的实践及技术。按以下步骤来创建:
- 创建带外观Shell的项目。创建一个初始的VS解决方案然后添加一个WPF 项目,该项目作为创建基于Prism类库解决方案的基础。称为外观Shell项目。
- 设置外观Shell窗口。在本任务中, 需要设置Shell 窗口以解耦的方式来寄宿不同用户界面 (UI) 组件。
- 设置应用程序的Bootstrapper。需要编码来设置初始化应用程序。
下面来创建一个外观Shell项目,外观Shell项目是一个典型基于Prism类库的项目的基础—它是一个包含了程序启动代码的WPF项目,使用了启动引导器BootStrapper,放置了典型视图的主窗体。
开始创建一个外观Shell项目
- 在VS中,创建一个名为HelloWorld.Desktop新的基于C#的WPF项目(省略创建步骤)。
如下图所示:
HelloWorld项目
在你的解决方案文件夹中创建一个名为Library.Desktop的项目文件夹,然后把CAL\Desktop\<Project>\bin\Debug中的程序集拷贝到上述文件夹下,包括:
- Microsoft.Practices.Composite.dll。该程序集包括核心组件如模块性组件、日志服务、通信服务,以及几个核心接口的定义,不包括UI组件元素。
- Microsoft.Practices.Composite.Presentation.dll。该程序集中包括WPF中的命令 commands、区域regions和事件的实现。
- Microsoft.Practices.Composite.UnityExtensions.dll。该程序集包括可重用的基础类、和使用了Unity Application Block的工具类。如BootStrapper基类、UnityBootstrapper基类, 用于创建和启动Unity容器及基本的Prism服务等。
- Microsoft.Practices.Unity.dll。 该程序集允许你在程序中使用Unity Application Block。基于Prism的项目默认可以使用Unity Application Block 或者MEF。开发者也可使用Prism类库的扩展来以适配的方式使用不同的容器。
- Microsoft.Practices.ServiceLocation.dll。该程序集包括Prism的公用服务定位器Service Locator 接口 来提供基于控件容器和服务定位器的反转的抽象;这样就可以很方便的改变容器的实现。
- 在Hello world项目中,添加上述程序集引用。
注意: |
拷贝程序集时,连同xml文件一起拷贝,就可以在开发时使用程序集智能感知。 |
外观Shell窗体是基于Prism类库的应用程序的顶级窗体。窗体用于寄宿不同的UI组件来展示或填充相应的视图。也可包含通用的UI组件,如菜单和工具栏等。
外观Shell窗体展示了应用程序的整体风格。
按下列步骤来设置外观Shell窗体:
- 在解决方案管理器中重命名 MainWindow.xaml 为Shell.xaml。
- 打开后台代码文件Shell.xaml.cs,用重构功能重命名MainWindow类名为Shell。
打开Shell.xaml 文件的Xaml视图, Window 根元素的下列属性:
- x:Class = "HelloWorld.Desktop.Shell" (与后台类名一致)
- Title = "Hello World"
Xaml代码如下:
<Window x:Class="HelloWorld.Desktop.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hello World" Height="300" Width="300">
<Grid>
</Grid>
</Window>
区域Regions
本步骤描述怎样添加 ItemsControl 控件l 到外观Shell窗体中并连接到区域Region中。接着你就可以动态地添加区域到外观窗体中。
步骤如下:
- 在Shell.xaml 文件中,添加下列命名空间,它们可以让你使用Prism类库中定义的区域Region的一些附加属性。
xmlns:cal="http://www.codeplex.com/CompositeWPF" - 然后把Xaml代码修改为如下:
<Window x:Class="HelloWorld.Desktop.Shell"
xmlns:cal="http://www.codeplex.com/CompositeWPF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hello World" Height="300" Width="300">
<ItemsControl Name="MainRegion"/>
</Window>
设计视图变为如下:
带ItemsControl控件的外观Shell窗体
- 修改ItemsControl 控件的属性cal:RegionManager.RegionName为"MainRegion"。代码如下:
<ItemsControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion"/>注意:启动外观窗体之后,WPF会解析附加属性cal:RegionManager.RegionName 并回调RegionManager 类。回调会创建一个与 ItemsControl 控件相关的区域。
启动引导器Bootstrapper
启动引导器Bootstrapper 负责初始化一个基于Prism类库构建的应用程序。包括 UnityBootstrapper 和MefBootstrapper 两张引导器类, 他们实现了大多数必要的功能用于使用Unity 或MEF 来作为你程序中的容器。当你使用Unity 或MEF之外的容器,你就要自己实现对应的启动引导器。
按如下步骤设置启动引导器BootStrapper。
- 添加新类Bootstrapper.cs文件到HelloWorld项目中。
- 添加要用到的命名空间以便使用UnityBootstrapper 类。
using System.Windows;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.UnityExtensions;
usingMicrosoft.Practices.Unity; -
添加继承关系
Class Bootstrapper : UnityBootstrapper
{
}
- 重载CreateShell方法 Bootstrapper 类。
protected override DependencyObject CreateShell()
{
return new Shell();
}
注意: |
返回的外观对象将UnityBootstrapper基类附加到区域管理器服务中。区域管理器服务是一个包含在 Prism中用于管理区域的服务。把区域管理器实例附加到外观窗体中, 你可显式注册Region到XAML 代码,然后它可以显示在外观窗体或视图中。 |
- 重载Bootstrapper中的InitializeShell方法,此方法用于向用户展示外观Shell。
protected override void InitializeShell()
{
base.InitializeShell();App.Current.MainWindow= (Window)this.Shell;App.Current.MainWindow.Show();
} - 重载ConfigureModuleCatalog方法。这个模板方法用于编制模块目录。 模块目录接口为Microsoft.Practices.Composite.Modularity.IModuleCatalog,另外它还为应用程序中所有模块提供元数据。本程序没有包含模块,所以ConfigureModuleCatalog方法的实现仅仅返回了基类的实现。粘贴如下代码到Bootstrapper 类中来实现该方法。
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
} - 更多模块加载和模块目录的细节参看 "任务3: 添加模块"。
- 打开文件 App.xaml.cs初始化Bootstrapper于Startup事件,如下所示。这样程序启动时,Bootstrapper的代码就会被执行。
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
} -
打开文件App.xaml 移除属性StartupUri,因为我们已经在外观窗口通过Bootstrapper手动实例化了Shell窗口,就不需要该属性了。代码如下:
<Application x:Class="HelloWorld.Desktop.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application> -
生成然后运行应用程序,就可以看到一个空的HelloWorld窗体,如下图所示。
Helloworld窗体
任务2: 添加模块
本任务中将添加一个模块到解决方案中,按如下步骤进行:
- 创建一个模块。 创建包含一个模块类的模块项目。
- 配置模块加载的方式。配置应用程序来加载模块。
参照下列步骤来创建模块:
- 添加一个新类库项目到解决方案中。 在解决方案管理器中右击HelloWorld.Desktop 解决方案,选择添加一个新项目,选择C#Windows类别,模板选择WPF用户控件类库。 项目名称设为HelloWorldModule,确定。如下图所示:
带HelloWorldModule模块项目的解决方案
- 添加模块项目HelloWorldModule需要用到的WPF相关的程序集引用:
- PresentationCore.dll
- PresentationFramework.dll
- WindowsBase.dll
- 添加模块项目HelloWorldModule需要用到的Prism相关的程序集引用:
- Microsoft.Practices.Composite.dll
- Microsoft.Practices.Composite.Presentation.dll
- 用重构的方式重命名Class1.cs文件为HelloWorldModule.cs(保证所有Class1都被重命名为HelloWorldModule)。
- 打开文件HelloWorldModule.cs,添加如下命名空间using:
using Microsoft.Practices.Composite.Modularity; - 修改代码以实现IModule接口:
public class HelloWorldModule : IModule
{
} - 在HelloWorldModule 类中添加一个空的Initialize方法:
public void Initialize()
{
} - 在HelloWorldModule项目中添加一个Views文件夹,用于存储所有Views的实现。
本步骤主要用于组织解决方案中的文件;这对一个包含很多老旧文件的项目来说是必要的。然后继续添加:
- Services文件夹。用于存储服务实现和服务接口。
- Controllers文件夹。用于存储控制器。
如下图所示:
HelloWorldModule解决方案
- 生成解决方案。
到目前为止,我们创建了一个基于Prism类库的并且带一个模块项目的解决方案。但模块还未加载到应用程序当中,下面的章节来继续讲述如何加载模块项目和Prism中的模块。
应用程序生命周期中的模块
在应用程序启动过程中,模块经过了三步处理:
- 通过模块目录发现模块。模块目录包含了模块元数据的集合。元数据可能被模块管理服务用到。
- 模块管理服务协调模块的初始化。 它先检索retrieval模块然后初始化。加载模块—必要时检索模块—验证模块。
- 最后模块管理服务实例化模块并调用Initialize 方法。
构建模块目录
TPrism类库提供了几种构建模块目录的方式。可以通过代码方式、 XAML 文件、配置文件或者通过一个文件夹来实现。下面讲述如何通过代码的方式来构建模块目录。
- 添加模块项目的引用到外观Shell项目中,即将HelloWorldModule的项目引用添加到HelloWorld.Desttop羡慕中。
- 打开文件Bootstrapper.cs找到ConfigureModuleCatalog方法并实现如下:
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
}
ModuleCatalog类以代码的方式定义应用程序模块—实现的内容包括实现IModuleCatalog 接口并添加一个AddModule方法手动注册模块以便在应用程序中加载模块。代码如下:
public ModuleCatalog AddModule(Type moduleType,InitializationMode initializationMode,params string[] dependsOn)
AddModule 方法同样返回模块目录实例和如下参数:
- 要加载的模块的初始化器类 initializer class的类型。该类型实现IModule 接口。
- 初始化的模式。该参数指示如何初始化一个模块。其值可能为InitializationMode.WhenAvailable 和InitializationMode.OnDemand。
- 包含所有模块依赖的模块名的数组。 在确保你的模块在模块的依赖加载后仍然有效,你的模块才会被加载。
- 更新ConfigureModuleCatalog 来注册带模块目录实例的HelloWorldModule模块。代码如下:
代码protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(HelloWorldModule.HelloWorldModule));
}
注意: |
本例中模块直接被Shell项目引用。 这使得可以用typeof(Module)来添加模块到目录中。但是在模块类型暂时无效时模块也可以被加载到目录中。 在没有指定特定的初始化模式时,WhenAvailable初始化模式是默认模式。 |
- 构建运行解决方案。确认HelloWorldModule模块初始化,加断点到HelloWorldModule类中 Initialize方法 。应用程序启动后会命中断点。
任务3: 添加视图
本任务将添加一个视图到HelloWorldModule 模块中。视图 Views 是指包含可视化内容的对象。视图Views 通常是用户控件,但也不是必须。 按如下步骤来添加视图:
- 创建视图。本步骤通过创建可视化内容来实现一个视图,并编码管理视图中的UI元素。
- 在区域region中展现视图。 本步骤将获取一个引用到区域,再加入一个视图到其中。
下面描述如何创建一个视图View
- 在模块中添加一个WPF用户控件,命名为HelloWorldView.xaml。
- 修改HelloWorldView.xaml代码为:
<UserControl x:Class="HelloWorldModule.Views.HelloWorldView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock Text="Hello World" Foreground="Green"HorizontalAlignment="Center" VerticalAlignment="Center"FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>
</Grid>
</UserControl> - 保存文件。
注意: |
本实验为求简单,没有用 Model-View-ViewModel模式来创建视图。 |
区域管理器Region Manager
区域管理器服务负责维护区域的集合,为控件创建新区域。该服务实现了Microsoft.Practices.Composite.Regions.IRegionManager 接口。 一般,我们直接与服务进行交互,以解耦的方式按名称来定位区域,并添加视图到区域中。默认情况下,UnityBootstrapper在应用程序容器中注册一个服务的实例。就是说你在HelloWorld应用程序中以依赖注入的方式获得一个到区域管理器服务的引用。
下面的步骤解释了如何获取区域管理器的实例,然后添加一个HelloWorldView视图 到外观Shell窗体的主区域main Region。
如下所示:
- 打开 HelloWorldModule.cs文件。
- 添加using语句
using Microsoft.Practices.Composite.Regions; - 创建一个只读的实例变量来保存到区域管理器的引用。
private readonly IRegionManager regionManager; - 修改HelloWorldModule 类的构造函数通过构造器依赖注入的方式获取一个区域管理器regionManager 实例变量。在构造函数中加入一个Microsoft.Practices.Composite.Regions.IRegionManager的参数。代码如下:
public HelloWorldModule(IRegionManager regionManager)
{
this.regionManager = regionManager;
} - 在Initialize 方法中,调用RegionManager实例中的RegisterViewWithRegion 方法。 Initialize方法注册一个区域名名称和与该区域相关的视图类型到一个区域视图注册表中;该注册表用于保存和检索这些映射。
RegisterViewWithRegion方法有两个重载。当你想直接注册一个视图,可以使用带两个参数的第一个重载,参数分别为区域名和视图类型。代码如下:
public void Initialize()
{
regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.HelloWorldView));
}在前述代码中用于UI构成的方法称为视图发现。使用该方法时,你需要指定视图和视图将被加载的区域。创建好一个区域之后,它会自动查找并加载相关的视图。
注意: |
区域名必须与定义在区域中的属性RegionName值一致。 |
- 生成并运行程序,会看到如下图所示的程序。
Hello World 消息