在实际应用开发中,随着项目业务逐渐复杂,耦合度会越来越高,维护成本也会直线上升,所以解耦也变得越来越重要。Prism框架为WPF开发中解耦提供了非常便捷的应用。今天主要以一个简单的小例子,简述WPF开发中Prism框架的简单应用,如有不足之处,还请指正。
什么是Prism?
Prism是一个开源框架,用于在WPF、Xamarin Forms、Uno/Win UI等应用中创建松耦合、可维护、可测试的XAML应用程序。Prism提供了一组设计模式的实现,这些设计模式有助于编写结构良好且可维护的XAML应用程序,包括MVVM,dependency injection,commands,EventAggregator等。
Prism源码库
Prism遵守开源许可协议(MIT),目前最新版本8.1.97,可通过GitHub进行下载最新版本。https://github.com/PrismLibrary
Prism优点
Prism 设计围绕核心建筑设计原则,即关注点分离和松散耦合。这使得Prism可以提供许多好处
- 重复使用:通过重复使用单元测试的组件,可以通过依赖性注入在运行时间轻松发现和集成,以及通过使用可在应用程序中重复使用的应用程序级功能封装模块,在应用级别实现重复使用。
- 可扩展性:通过管理组件依赖性、使组件在运行时间更容易集成或替换为替代实现以及提供将应用程序分解为可独立更新和部署的模块的能力,帮助创建易于扩展的应用程序
- 灵活性:Prism 有助于创建灵活的应用程序,使它们能够随着新功能的开发和集成而更容易更新
- 团队发展:Prism 有助于最大限度地减少跨团队依赖性,并允许团队专注于不同的功能领域(如 UI 设计、业务逻辑实现和基础架构代码开发),或不同业务级别的功能领域(如简介、销售、库存或物流)。
模块化思想
通过对比发现,采用模块化思想进行设计,使得程序结构清晰,符合高内聚,低耦合的设计风格。
Prism安装
Prism可通过NuGet方案包管理器进行安装,主要安装三个Prism.Core,Prism.Unity,Prism.Wpf
创建模块和视图控件
创建WPF类库,并添加用户控件视图,并采用MVVM开发模式
数据绑定
在Prism框架中,提供了数据绑定基类Prism.Mvvm.BindableBase,可以方便的将普通属性,转换为依赖属性,简化开发中过程中的代码量。
namespace DemoPrism.Second.ViewModels { internal class SecondViewModel : BindableBase { #region 属性及构造函数 private int id; public int Id { get { return id; } set { SetProperty(ref id, value); } } /// <summary> /// 模块间交互 /// </summary> private readonly IEventAggregator eventAggregator; public SecondViewModel(IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator; this.eventAggregator.GetEvent<DemoOneEvent>().Subscribe(DemoOneRecived); } #endregion private void DemoOneRecived(int id) { this.Id = id; } } }
创建Prism模块
添加Module类,并实现Prism.Modularity.IModule接口,实现接口的模块,视为可以被Prism发现并加载的模块。以DefectListModule模块为例:
namespace DemoPrism.First { public class FirstModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { IRegionManager regionManager = containerProvider.Resolve<IRegionManager>(); regionManager.RegisterViewWithRegion("FirstRegion", typeof(Views.FirstView)); } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<Views.FirstView, ViewModels.FirstViewModel>(); } } }
模块配置
Prism提供了多种模块加载方式,常用的有App.config配置文件方法。
- 在App.config节点,添加configSections配置,增加modules节点配置
- modules节点主要配置需要加载的Prism模块
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <!--prism配置--> <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/> </configSections> <modules> <!--注册模块--> <module assemblyFile="Modules\DemoPrism.First.dll" moduleType="DemoPrism.First.FirstModule, DemoPrism.First" moduleName="First" startupLoaded="true" /> <module assemblyFile="Modules\DemoPrism.Second.dll" moduleType="DemoPrism.Second.SecondModule, DemoPrism.Second" moduleName="Second" startupLoaded="true" /> </modules> </configuration>
模块加载
模块配置好后,需要在启动的时候,加载模块。修改WPF入口启动程序,App.xaml.cs文件,继承自Prism.Unity.PrismApplication基类,并重写相关初始化
namespace DemoPrism { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { //使用容器创建主窗体 protected override Window CreateShell() => Container.Resolve<MainWindow>(); protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { //通过代码的方式添加模块 //moduleCatalog.AddModule<NavigationModule.NavigationModule>(); //将MedicineModule模块设置为按需加载 base.ConfigureModuleCatalog(moduleCatalog); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override IModuleCatalog CreateModuleCatalog() { ConfigurationModuleCatalog configurationModuleCatalog = new ConfigurationModuleCatalog(); configurationModuleCatalog.Load(); //通过Xaml配置文件读取模块加载信息 return configurationModuleCatalog; //return directoryModuleCatalog; } /// <summary> /// 注册适配器(区域容器:Region) /// </summary> /// <param name="regionAdapterMappings"></param> protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings) { base.ConfigureRegionAdapterMappings(regionAdapterMappings); } } }
区域Region
在Prism框架中,模块可以注册到导航菜单Navigation,也可以注册到区域Region,根据实际业务需要进行选择。Region可以更加方便的进行模块化布局等。在普通容器控件中,增加prism:RegionManager.RegionName=”名称”
<Window x:Class="DemoPrism.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prism="http://prismlibrary.com/" xmlns:local="clr-namespace:DemoPrism" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" prism:ViewModelLocator.AutoWireViewModel="True"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <ContentControl Grid.Column="0" prism:RegionManager.RegionName="FirstRegion"></ContentControl> <ContentControl Grid.Column="1" prism:RegionManager.RegionName="SecondRegion"></ContentControl> </Grid> </Window>
模块交互
模块与模块之间相互独立,如果需要交互,可以通过事件聚合器IEventAggregator,采用事件的订阅和发布进行通信。
事件订阅步骤:
- 定义事件,定义一个类,继承自Prism.Events.PubSubEvent泛型类
- 事件发布,通过事件聚合器的Publish方法进行发布。
- 事件订阅,通过事件聚合器的Subscribe进行订阅。
namespace DemoPrism.Event { /// <summary> /// 注册事件 /// </summary> public class DemoOneEvent : PubSubEvent<int> { } }
弹出模态窗口
在Prism框架下,弹出模态窗口,需要以下3个步骤:
- 在Prism框架中,页面UserControl实现弹窗功能,被弹出页面需要实现Prism.Services.Dialogs.IDialogAware接口。
- 注册窗口,将UserControl注册成窗口。
- 调用弹出服务,弹出窗口