tnblog
首页
视频
资源
登录

WPF Prism 框架:打造高效、可维护的 WPF 应用

461人阅读 2025/4/22 15:32 总访问:3662737 评论:0 收藏:0 手机
分类: .net后台框架

WPF Prism 框架:打造高效、可维护的 WPF 应用

Prism 框架简介


Prism 是一个用于构建松耦合、可维护且可测试的 XAML 应用程序的框架,支持 WPF、.NET MAUI、Uno Platform 和 Xamarin Forms 等多个平台。
它提供了多种设计模式的实现,如 MVVM(Model-View-ViewModel)、依赖注入、命令、事件聚合器等,这些模式有助于编写结构良好且易于维护的 XAML 应用程序。

Prism 框架关键库

库名 描述
Prism.Core 实现MVVM的核心功能,属于一个与平台无关的项目。
Prism.WPF 包含了DialogService、Region、Module、Navigation,其他的一些WPF的功能
Prism.Unity Prism.Unity 是 Prism 框架的一个扩展,它集成了 Unity 依赖注入容器。


这里我创建了一个LearningPrism项目,并安装好了这三个包,如下图所示:

  1. <ItemGroup>
  2. <PackageReference Include="Prism.Core" Version="9.0.537" />
  3. <PackageReference Include="Prism.Unity" Version="9.0.537" />
  4. <PackageReference Include="Prism.Wpf" Version="9.0.537" />
  5. </ItemGroup>

BindableBase


BindableBase 是 Prism 提供的一个基类,实现了 INotifyPropertyChanged 接口。
它简化了属性变更通知的触发逻辑,确保 UI 能够自动响应数据变化。

INotifyDataErrorInfo


INotifyDataErrorInfo 是 WPF 4.0 引入的一个接口,用于实现自定义的数据验证逻辑。它包含以下三个成员:

方法 描述
bool HasErrors 一个只读属性,用于指示是否存在错误。
event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged 一个事件,用于通知绑定的控件错误信息已更改。
IEnumerable GetErrors(string propertyName) 一个方法,用于获取指定属性的错误信息。


下面是一个简单的例子,当Value大于100报错。

  1. public class MainViewModel : BindableBase, INotifyDataErrorInfo
  2. {
  3. private int _value = 100;
  4. public int Value
  5. {
  6. get { return _value; }
  7. set {
  8. if (value > 100)
  9. {
  10. ErrorsContainer.SetErrors("Value",new string[] { "值不能大于100" });
  11. }
  12. SetProperty(ref _value, value);
  13. }
  14. }
  15. /// <summary>
  16. /// 定义 OnErrorsContainerCreate 方法,用于在 ErrorsContainer 中添加错误时触发
  17. /// </summary>
  18. /// <param name="arg"></param>
  19. private void OnErrorsContainerCreate(string arg)
  20. {
  21. // 触发 ErrorsChanged 事件,通知 UI 错误信息已更改
  22. ErrorsChanged?.Invoke(this,new DataErrorsChangedEventArgs(arg));
  23. }
  24. /// <summary>
  25. /// 定义一个私有字段 _errorsContainer,用于存储错误信息
  26. /// </summary>
  27. private ErrorsContainer<string> _errorsContainer;
  28. public ErrorsContainer<string> ErrorsContainer
  29. {
  30. get {
  31. if (_errorsContainer == null)
  32. {
  33. _errorsContainer = new ErrorsContainer<string>(OnErrorsContainerCreate);
  34. }
  35. return _errorsContainer;
  36. }
  37. set { _errorsContainer = value; }
  38. }
  39. /// <summary>
  40. /// 判断是否存在异常
  41. /// </summary>
  42. public bool HasErrors => ErrorsContainer.HasErrors;
  43. /// <summary>
  44. /// 用于通知 UI 错误信息已更改
  45. /// </summary>
  46. public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
  47. /// <summary>
  48. /// 用于获取指定属性的错误信息
  49. /// </summary>
  50. /// <param name="propertyName"></param>
  51. /// <returns></returns>
  52. public IEnumerable GetErrors(string? propertyName)=> ErrorsContainer.GetErrors(propertyName);
  53. }


修改MainWindow.xaml

  1. <Window x:Class="LearningPrism.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:local="clr-namespace:LearningPrism"
  7. mc:Ignorable="d"
  8. Title="MainWindow" Height="450" Width="800">
  9. <!-- 设置窗口的数据上下文为 MainViewModel -->
  10. <Window.DataContext>
  11. <local:MainViewModel/>
  12. </Window.DataContext>
  13. <!-- 定义一个资源字典,用于存储控件模板 -->
  14. <Window.Resources>
  15. <!-- 定义一个 ControlTemplate,用于自定义 TextBox 的样式 -->
  16. <ControlTemplate TargetType="{x:Type TextBox}" x:Key="ct">
  17. <Grid>
  18. <Grid.RowDefinitions>
  19. <RowDefinition Height="auto"/>
  20. <RowDefinition Height="auto"/>
  21. </Grid.RowDefinitions>
  22. <!-- 定义一个 Border,用于显示 TextBox 的边框 -->
  23. <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
  24. Background="{TemplateBinding Background}" SnapsToDevicePixels="True"
  25. CornerRadius="5">
  26. <!-- 定义一个 ScrollViewer,用于显示 TextBox 的内容 -->
  27. <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"
  28. VerticalContentAlignment="Center" Margin="3,5" BorderThickness="0"/>
  29. </Border>
  30. <!-- 定义一个 TextBlock,用于显示错误信息 -->
  31. <TextBlock Grid.Row="1" Text="{Binding (Validation.Errors)[0].ErrorContent,RelativeSource={RelativeSource AncestorType=TextBox,Mode=FindAncestor}}"
  32. Foreground="Red" Margin="10,5"
  33. Name="txtError"/>
  34. </Grid>
  35. <!-- 定义触发器,用于在 TextBox 验证失败时显示错误信息 -->
  36. <ControlTemplate.Triggers>
  37. <Trigger Property="Validation.HasError" Value="True">
  38. <Setter Property="Visibility" Value="Visible" TargetName="txtError"/>
  39. <Setter Property="ToolTip"
  40. Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
  41. </Trigger>
  42. </ControlTemplate.Triggers>
  43. </ControlTemplate>
  44. </Window.Resources>
  45. <Grid>
  46. <StackPanel>
  47. <TextBlock Text="{Binding Value}"/>
  48. <TextBox Text="{Binding Value,UpdateSourceTrigger=PropertyChanged}" Template="{StaticResource ct}"/>
  49. </StackPanel>
  50. </Grid>
  51. </Window>


当我们进行修改100为110时,它将会报错。

行为处理

DelegateCommand


DelegateCommand 是 Prism 框架中提供的一个命令类(如果你没有使用 Prism 框架,可能也会有类似的自定义命令类来实现类似功能),它用来表示一个可以执行的操作(命令)。
DelegateCommand 包含了两个重要的部分:
Execute:表示命令被执行时要执行的方法(后面会看到绑定的 Execute 方法)。
CanExecute:用于判断命令是否可以执行,根据这个方法的返回值来决定绑定的按钮是否启用(是否变灰)。

我们将在MainViewModel类中定义一个 DelegateCommand 类型的属性 BtnCheckCommand

  1. public DelegateCommand BtnCheckCommand { get; }


接着我们定义命令执行逻辑

  1. private bool CheckExecute()
  2. {
  3. return Value == 100;
  4. }
  5. private void Execute()
  6. {
  7. this.Value = 0;
  8. }


CheckExecute 方法是 CanExecute 的具体实现,它会根据 Value 的值来判断命令是否可以执行。
如果 Value 的值等于 100,则返回 true 表示命令可以执行;否则返回 false,表示命令不能执行。
Execute 方法是命令被执行时的操作,也就是当按钮可以点击并且被点击时,会调用这个方法。
这里简单地将 Value 的值设置为 0。
接着我们初始化命令

  1. public MainViewModel()
  2. {
  3. BtnCheckCommand = new DelegateCommand(Execute,CheckExecute);
  4. }


我们给值属性添加一行代码,这行代码的作用是通知所有与这个命令相关的控件(比如按钮),命令的可执行状态可能发生了变化,让它们重新检查命令是否可以执行。

  1. public int Value
  2. {
  3. get { return _value; }
  4. set {
  5. if (value > 100)
  6. {
  7. ErrorsContainer.SetErrors("Value",new string[] { "值不能大于100" });
  8. }
  9. SetProperty(ref _value, value);
  10. // 触发命令相关的可执行的检查逻辑
  11. BtnCheckCommand.RaiseCanExecuteChanged();
  12. }
  13. }


接着我们在窗体绑定命令按钮。
然后运行测试。

  1. <Button Content="检查" Command="{Binding BtnCheckCommand}"></Button>


我们将点击检查,发现值等于100,然后将100改成0后,按钮将被禁用了。

检查方式


除了上面的BtnCheckCommand.RaiseCanExecuteChanged();检查方式我们还有其他两种方式。
ObservesProperty 是 Prism 框架中用于简化命令的 CanExecute 方法和属性变化通知的一种特性。它可以帮助你自动触发命令的 CanExecuteChanged 事件,而无需手动调用 RaiseCanExecuteChanged()。下面是如何使用 ObservesProperty 来实现命令的自动检测:

  1. public MainViewModel()
  2. {
  3. BtnCheckCommand = new DelegateCommand(Execute,CheckExecute)
  4. .ObservesProperty(()=>Value);
  5. }
  1. // 触发命令相关的克执行的检查逻辑
  2. //BtnCheckCommand.RaiseCanExecuteChanged();


我们还可以使用ObservesCanExecute来进行检测。
但是ObservesCanExecute 方法只能用于观察一个简单的布尔属性,所以我们这里定义一个状态来解决。

  1. private bool CheckExecute()
  2. {
  3. return Value == 100;
  4. }
  5. private void Execute()
  6. {
  7. this.Value = 0;
  8. Status = !Status;
  9. }
  10. private bool _status;
  11. public bool Status {
  12. get => Value == 100;
  13. set {
  14. SetProperty(ref _status,value);
  15. }
  16. }
  17. public MainViewModel()
  18. {
  19. BtnCheckCommand = new DelegateCommand(Execute,CheckExecute)
  20. //.ObservesProperty(()=>Value)
  21. .ObservesCanExecute(()=> Status)
  22. ;
  23. }

异步命令


异步命令非常简单,就只是在普通命令的方法处理上面添加上async就可以了。

  1. public DelegateCommand BtnAsyncCommand { get=>new DelegateCommand(DoAsyncSoming); }
  2. private async void DoAsyncSoming()
  3. {
  4. await Task.Delay(3000);
  5. this.Value = 30;
  6. }


在窗体我们添加一个异步按钮。

  1. <Button Content="异步" Command="{Binding BtnAsyncCommand}"></Button>

泛型参数


首先实例化一个泛型DelegateCommand类,这里我们以string作为一个参数进行实现。

  1. public ICommand BtnPpCommand { get => new DelegateCommand<string>(DoPpSoming); }
  2. private async void DoPpSoming(string obj)
  3. {
  4. await Task.Delay(3000);
  5. this.Value = 40;
  6. }


前端我们通过CommandParameter进行传入参数

  1. <Button Content="传参" Command="{Binding BtnPpCommand}" CommandParameter="传递的参数"></Button>

事件命令


我们这里用一个ComboBox做一个示范,当我们进行选择时将触发该事件。
首先我们在Windows节点中添加下面两个引用。

  1. xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
  2. xmlns:prism="http://prismlibrary.com/"


添加下面的ComboBox代码

  1. <!-- 定义一个 ComboBox 控件,默认选中第一个选项 -->
  2. <ComboBox SelectedIndex="0">
  3. <!-- 设置交互触发器,用于在发生特定事件时执行相应动作 -->
  4. <i:Interaction.Triggers>
  5. <!-- 指定在 ComboBox 的 SelectionChanged 事件发生时触发动作 -->
  6. <i:EventTrigger EventName="SelectionChanged">
  7. <!-- 调用视图模型中的命令 BtnEventCommand -->
  8. <prism:InvokeCommandAction Command="{Binding BtnEventCommand}">
  9. </prism:InvokeCommandAction>
  10. </i:EventTrigger>
  11. </i:Interaction.Triggers>
  12. <!-- 向 ComboBox 添加可选择的项目 -->
  13. <ComboBoxItem Content="11111" />
  14. <ComboBoxItem Content="22222" />
  15. <ComboBoxItem Content="33333" />
  16. <ComboBoxItem Content="44444" />
  17. <ComboBoxItem Content="55555" />
  18. </ComboBox>


添加一下BtnEventCommand命令。

  1. public ICommand BtnEventCommand { get => new DelegateCommand<object>(DoEventSoming); }
  2. private void DoEventSoming(object obj)
  3. {
  4. }


选择后我们发现,它可以接收到我们的参数。


如果我们只希望接收到Source.SelectedItem.Content中的参数的话可以这样写。

  1. <prism:InvokeCommandAction Command="{Binding BtnEventCommand}" TriggerParameterPath="Source.SelectedItem.Content"/>


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

WPF Prism 框架初始化

WPF Prism 框架初始化[TOC] 什么是 Prism Bootstrapper?Prism Bootstrapper 是一个抽象类,它定义了一个基本的启动序列,...

WPF Prism ViewModel的应用

WPF Prism ViewModel的应用[TOC] 在 WPF 开发中,Prism 是一个非常流行的框架,它基于 MVVM(Model-View-ViewModel)模式...

WPF Prism Dialog与Region

WPF Prism Dialog与Region[TOC] Prism框架中的Dialog子窗口处理在 WPF 应用程序开发中,对话框是一个常见的功能需求,无论...

WPF Prism 复合命令与模块化管理

WPF Prism 复合命令与模块化管理[TOC] Prism 中的 CompositeCommand 示例CompositeCommand 是 Prism 框架中用于组合多个子...

Java框架

第三阶段:Java框架第一部分:Maven1. Maven环境搭建2. Maven构建项目3. Maven本地仓库配置4. Maven中央仓库配置5. Maven基...

使用Refit框架访问REST接口

改装是一个类型安全的REST开源库,是一套基于RESTful架构的.NET客户端实现,内部使用HttpClient类封装,可通过改装更加简单...

Web前段框架技术之全选,全不选,反选并显示出来

我们用到&lt;input&gt;标签,再每一个&lt;input&gt;标签取一个相同的name值。如下图:开始我们的骚操作,用Web前段框架技术...

02--框架与库的区别

框架:是一套完整的解决方案; 对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。node 中的 express;...

.NET MVC EF框架中Left Join的SelectMany原理

下列代码为实现Left join 查询数据库,如果在调用SelectMany方法之前已经ToList,返回之后就结束操作数据库了,SelectMany ...

.net Core FluentValidation 验证框架

1.前言 (功能与特点)功能:验证字段的属性大小特点:验证逻辑与业务逻辑分离 灵活,功能强大 (使用Fluent API,Lambda表...

mui框架-移动端跳转以及传值的简单方法(修改解决方法)

纠结了两天的MUI跳转的问题,终于解决了 ,现在分享给大家,希望大家有什么坑的解决也给我分享分享 哈哈,废话不多说,上代...

MUI框架页面间的传值指针

把时髦的技术挂在嘴边,不如把过时的技术记在心里。 今天我们说的是关于MUI,一个封装了Html5+的框架间页面传值问题一,在h...

关于System.InvalidOperationException:“未找到具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序的实体框架提供程序...

C#的后台遇到这种错误 System.InvalidOperationException:“未找到具有固定名称“System.Data.SqlClient”的 ADO.NET 提供...

.net core 3.0 之Api文档生成 NSwag框架

前言是另一个用于生成 Swagger 文档并将Swagger UI或ReDoc集成到 ASP.NET Core Web API 中的开源项目。此外,NSwag 还提供...

abp框架的介绍和基用法

一、什么是ABP框架? ABP框架全称为“ASP.NETBoilerplateProject”,中文翻译为“ASP.NET样板项目”,诞生的主要目的就是...
这一世以无限游戏为使命!
排名
5
文章
229
粉丝
15
评论
7
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术