草庐IT

WPF开发之Prism详解【内附源码】

老码识途 2023-03-28 原文

在实际应用开发中,随着项目业务逐渐复杂,耦合度会越来越高,维护成本也会直线上升,所以解耦也变得越来越重要。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,可以方便的将普通属性,转换为依赖属性,简化开发中过程中的代码量。

 1 namespace DemoPrism.Second.ViewModels
 2 {
 3     internal class SecondViewModel : BindableBase
 4     {
 5         #region 属性及构造函数
 6 
 7         private int id;
 8 
 9         public int Id
10         {
11             get { return id; }
12             set { SetProperty(ref id, value); }
13         }
14 
15         /// <summary>
16         /// 模块间交互
17         /// </summary>
18         private readonly IEventAggregator eventAggregator;
19 
20         public SecondViewModel(IEventAggregator eventAggregator)
21         {
22             this.eventAggregator = eventAggregator;
23             this.eventAggregator.GetEvent<DemoOneEvent>().Subscribe(DemoOneRecived);
24         }
25         #endregion
26         private void DemoOneRecived(int id)
27         {
28             this.Id = id;
29         }
30     }
31 }

创建Prism模块

添加Module类,并实现Prism.Modularity.IModule接口,实现接口的模块,视为可以被Prism发现并加载的模块。以DefectListModule模块为例:

 1 namespace DemoPrism.First
 2 {
 3     public class FirstModule : IModule
 4     {
 5         public void OnInitialized(IContainerProvider containerProvider)
 6         {
 7             IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
 8             regionManager.RegisterViewWithRegion("FirstRegion", typeof(Views.FirstView));
 9         }
10 
11         public void RegisterTypes(IContainerRegistry containerRegistry)
12         {
13             containerRegistry.RegisterForNavigation<Views.FirstView, ViewModels.FirstViewModel>();
14         }
15     }
16 }

模块配置

Prism提供了多种模块加载方式,常用的有App.config配置文件方法。

  1. 在App.config节点,添加configSections配置,增加modules节点配置

  2. modules节点主要配置需要加载的Prism模块

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <configSections>
 4     <!--prism配置-->
 5     <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
 6   </configSections>
 7   <modules>
 8     <!--注册模块-->
 9     <module assemblyFile="Modules\DemoPrism.First.dll" moduleType="DemoPrism.First.FirstModule, DemoPrism.First" moduleName="First" startupLoaded="true" />
10     <module assemblyFile="Modules\DemoPrism.Second.dll" moduleType="DemoPrism.Second.SecondModule, DemoPrism.Second" moduleName="Second" startupLoaded="true" />
11   </modules>
12 </configuration>

模块加载

模块配置好后,需要在启动的时候,加载模块。修改WPF入口启动程序,App.xaml.cs文件,继承自Prism.Unity.PrismApplication基类,并重写相关初始化

 

 

 1 namespace DemoPrism
 2 {
 3     /// <summary>
 4     /// Interaction logic for App.xaml
 5     /// </summary>
 6     public partial class App : PrismApplication
 7     {
 8         //使用容器创建主窗体
 9         protected override Window CreateShell() => Container.Resolve<MainWindow>();
10 
11 
12         protected override void ConfigureViewModelLocator()
13         {
14             base.ConfigureViewModelLocator();
15         }
16 
17         protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
18         {
19             //通过代码的方式添加模块
20             //moduleCatalog.AddModule<NavigationModule.NavigationModule>();
21             //将MedicineModule模块设置为按需加载
22             base.ConfigureModuleCatalog(moduleCatalog);
23         }
24 
25         protected override void RegisterTypes(IContainerRegistry containerRegistry)
26         {
27 
28         }
29 
30 
31         protected override IModuleCatalog CreateModuleCatalog()
32         {
33             ConfigurationModuleCatalog configurationModuleCatalog = new ConfigurationModuleCatalog();
34             configurationModuleCatalog.Load();
35 
36             //通过Xaml配置文件读取模块加载信息
37 
38             return configurationModuleCatalog;
39             //return directoryModuleCatalog;
40         }
41 
42         /// <summary>
43         /// 注册适配器(区域容器:Region)
44         /// </summary>
45         /// <param name="regionAdapterMappings"></param>
46         protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
47         {
48             base.ConfigureRegionAdapterMappings(regionAdapterMappings);
49         }
50     }
51 }

区域Region

在Prism框架中,模块可以注册到导航菜单Navigation,也可以注册到区域Region,根据实际业务需要进行选择。Region可以更加方便的进行模块化布局等。在普通容器控件中,增加prism:RegionManager.RegionName=”名称”

 

 

 

 1 <Window x:Class="DemoPrism.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:prism="http://prismlibrary.com/"
 7         xmlns:local="clr-namespace:DemoPrism"
 8         mc:Ignorable="d"
 9         Title="MainWindow" Height="450" Width="800"
10         prism:ViewModelLocator.AutoWireViewModel="True">
11     <Grid>
12         <Grid.ColumnDefinitions>
13             <ColumnDefinition></ColumnDefinition>
14             <ColumnDefinition></ColumnDefinition>
15         </Grid.ColumnDefinitions>
16         <ContentControl Grid.Column="0" prism:RegionManager.RegionName="FirstRegion"></ContentControl>
17         <ContentControl Grid.Column="1" prism:RegionManager.RegionName="SecondRegion"></ContentControl>
18     </Grid>
19 </Window>

模块交互

模块与模块之间相互独立,如果需要交互,可以通过事件聚合器IEventAggregator,采用事件的订阅和发布进行通信。

事件订阅步骤:

  1. 定义事件,定义一个类,继承自Prism.Events.PubSubEvent泛型类

  2. 事件发布,通过事件聚合器的Publish方法进行发布。

  3. 事件订阅,通过事件聚合器的Subscribe进行订阅。

 1 namespace DemoPrism.Event
 2 {
 3     /// <summary>
 4     /// 注册事件
 5     /// </summary>
 6     public class DemoOneEvent : PubSubEvent<int>
 7     {
 8 
 9     }
10 }

弹出模态窗口

 在Prism框架下,弹出模态窗口,需要以下3个步骤:

  1. 在Prism框架中,页面UserControl实现弹窗功能,被弹出页面需要实现Prism.Services.Dialogs.IDialogAware接口。

  2. 注册窗口,将UserControl注册成窗口。

  3. 调用弹出服务,弹出窗口

源码下载

示例中源码下载,在公众号恢复关键词PRISM,如下所示:

 

 

学习编程,从关注【老码识途】开始!!!

 

有关WPF开发之Prism详解【内附源码】的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  9. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  10. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

随机推荐