一、简介
WPF Prism是一套由微软模式和实践团队(Microsoft Patterns & Practices Team)开发的面向Windows Presentation Foundation (WPF) 的框架。该框架目标是带来可重用、可插入、可测试的模块化代码,以及保持松耦合的应用程序代码。WPF Prism框架用于帮助开发人员构建具有高度模块化、可测试、可维护且易于扩展的WPF桌面应用程序。Prism利用依赖注入 (DI) 容器来实现模块化、命令、事件聚合 (EA)、导航、区域等功能,以提供一致性的解决方案。
二、模块化开发
设计良好的应用程序应该是由若干松耦合的模块组成的,每个模块都能独立、可移植、可重用,并且易于测试。Prism框架通过依赖注入(DI)容器实现模块化,代码创建对象时只需指定对象基类或接口,DI容器会自动寻找并构造该对象所依赖的实例,并返回完整的对象。
实际上,在Prism框架上,模块等同于程序集,而且每个程序集都必须包含一个派生自 IModule
接口的模块类。这个类需要实现 IModule.Initialize
方法,通过该方法可以向程序注入一些功能。下面介绍一些重要的基本模块初始化,以及在模块之间共享数据。
1. 模块初始化
使用Prism框架,您可以使用以下代码,初始化模块
// MyApp.ModulA.dll
public class ModulA : IModule
{
public void Initialize()
{
// 初始化视图和服务所需的代码
}
}
// App.xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// 使用RegisterType注册服务
containerRegistry.RegisterType<IService, Service>();
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
// 使用ModuleCatalog添加模块
moduleCatalog.AddModule(typeof(ModulA));
}
2. 模块间共享数据
在模块之间,有时需要共享数据。例如,ModulA
模块需要提供数据,以便其他模块使用,您可以使用以下代码,向其他需要该数据的模块添加服务
// MyApp.ModulA.dll
public class ModulA : IModule
{
// 公共数据
public static IData Data = new Data();
public void Initialize()
{
// 注册服务
containerRegistry.RegisterInstance<IData>(Data);
}
}
// MyApp.ModulB.dll
public class ModulB : IModule
{
public ModulB(IData data)
{
var myData = data as Data;// 编写代码,以便使用Data服务
}
public void Initialize()
{
// 初始化...
}
}
// App.xaml.cs
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModulA>();// 我们不需要在此处添加ModulB,因为ModulB是自动加载的
}
三、依赖注入
依赖注入(Dependency Injection)是一种编程方法,通过该方法,我们可以创建松耦合、可测试且可重用的代码。您只需定义接口或基类,然后使用Prism框架,自动寻找并创建该接口或基类的实现。依赖注入是Prism框架的核心,贯穿整个框架。
1. 构造函数注入
构造函数注入是最简单的依赖注入方法。在该模式下,您仅需使用 Container.Resolve
方法创建实例。以下代码演示使用构造函数注入实例化对象
// 定义接口和实现
public interface IService
{
void DoSomething();
}
public class Service : IService
{
public void DoSomething()
{
Console.WriteLine("Hello World!");
}
}
// Main
var service = Container.Resolve<IService>();
service.DoSomething();
2. 属性注入
属性注入是一种广泛应用的依赖注入方法。该方法在对象创建完成之后,通过反射操作,向对象的属性自动注入相应的实现实例。通过Prism框架,我们使用 [Dependency]
特性注入属性。
// 定义接口和实现
public interface IService
{
void DoSomething();
}
public class Service : IService
{
public void DoSomething()
{
Console.WriteLine("Hello World!");
}
}
// Main
class MainViewModel
{
[Dependency]
public IService Service { get; set; } // 必须是公共属性
public MainViewModel()
{
}
public void DoSomething()
{
Service.DoSomething();
}
}
// 在Main函数中初始化
var mainViewModel = new MainViewModel();
Container.BuildUp(mainViewModel); // 执行依赖注入
mainViewModel.DoSomething();
四、命令
在Prism框架中,命令(Command)是一项非常强大的功能,它可以轻松解决许多典型的WPF应用程序需求,例如向视图添加命令,创建分离的命令对象,将命令绑定到控件或 ViewModel 等。
在Prism框架内部,命令使用 DelegateCommand / DelegateCommand<T>
类来实现。例如,以下是使用 DelegateCommand
类创建命令的示例代码
// 定义命令
public DelegateCommand MyCommand { get; private set; }
public MainViewModel()
{
// 添加命令
MyCommand = new DelegateCommand(Execute, CanExecute);
}
void Execute()
{
Debug.WriteLine("执行命令");
}
bool CanExecute()
{
return true;
}
五、导航
在WPF应用程序中,导航(navigation)是一项关键的功能。该功能使我们能够通过显示给定的页面或视图,让用户在视觉上移动,以便在不同的屏幕之间进行切换。在Prism框架中,导航需要两个主要组件: 导航器(NavigationService)和导航目的地(NavigationTarget)。
导航器处理导航请求,而导航目的地则对导航请求进行响应。
// 在模块中添加导航
public class AppModule : IModule
{
public AppModule(INavigationService navigationService)
{
this.navigationService = navigationService;
}
public void Initialize()
{
navigationService.RegisterViewWithRegion("MainRegion", typeof(ViewA));
}
}
// 在ViewModel中实现导航
public class ViewAViewModel : BindableBase // BindableBase是Prism中的一个基类,实现了 INotifyPropertyChanged
{
public ViewAViewModel(INavigationService navigationService)
{
NavigateCommand = new DelegateCommand(() => navigationService.Navigate("ViewB"));
}
private DelegateCommand _navigateCommand;
public DelegateCommand NavigateCommand
{
get => _navigateCommand;
set => SetProperty(ref _navigateCommand, value);
}
}
// 在View中,创建一个按钮并将它绑定到导航命令
// 显示View
navigationService.Navigate("ViewA");
六、区域
在WPF Prism中,区域(Region)是指一个可以在其中显示一个或多个视图的窗体区域。您可以将区域视为桌面应用程序中的子窗口,因为您可以将视图放入区域,并在不同的区域中切换视图。
以下示例演示如何创建区域,并将视图添加到该区域
// App.xaml.cs
public partial class App : PrismApplication
{
protected override Control CreateShell()
{
return Container.Resolve
();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register();
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule
();
}
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve
());
}
}
// 注册StackPanel区域和Adapter
public class StackPanelRegion : Region
{
}
public class StackPanelRegionAdapter : IRegionAdapter
{
public IRegion Initialize(object regionTarget, string regionName)
{
var region = new StackPanelRegion();
region.Name = regionName;
var stackpanel = regionTarget as StackPanel;
RegionManager.SetRegionManager(stackpanel, ContainerLocator.Current.Resolve
());
RegionManager.UpdateRegions();
return region;
}
public bool IsAdaptable(object regionTarget)
{
return regionTarget is StackPanel;
}
public void AdaptView(object regionTarget)
{
}
}
// 主模块
public class MainModule : IModule
{
public void Initialize()
{
IRegionManager regionManager = ServiceLocator.Current.GetInstance
(); regionManager.RegisterViewWithRegion("TopArea", typeof(TopView)); } } // 创建具有指定名称的区域,以及包含“TopArea”名称的StackPanel控件
七、总结
通过本文的介绍,我们可以看到WPF Prism框架的强大功能:模块化开发、依赖注入、命令、导航和区域。通过这些功能,我们可以轻松地构建可扩展、可重用、可测试的WPF应用程序。当然,除了本文介绍的这些内容之外,WPF Prism框架还提供了许多其他的功能和特性,如事件聚合(event aggregator),属性注入、策略等。