tnblog
首页
视频
资源
登录

WPF Prism ViewModel的应用

374人阅读 2025/4/25 14:52 总访问:3662440 评论:0 收藏:0 手机
分类: .net后台框架

WPF Prism ViewModel的应用


在 WPF 开发中,Prism 是一个非常流行的框架,它基于 MVVM(Model-View-ViewModel)模式,提供了一套强大的工具和模式来构建复杂、可维护、可扩展的应用程序。
本文将深入探讨如何在 WPF 中使用 Prism 的 ViewModel,实现视图与数据的优雅交互。

项目结构的组织


在开始之前,我们需要合理组织项目结构,以确保代码的清晰和可维护性。一个典型的项目结构如下:
Views 目录:存放所有的视图文件(如 MainWindow.xaml),这些文件负责界面的展示。
ViewModels 目录:存放所有的视图模型文件(如 MainWindowViewModel.cs),这些文件负责业务逻辑的实现。

示例代码


MainWindowViewModel.cs

  1. namespace LearningPrismUnityIoC.ViewModels
  2. {
  3. public class MainWindowViewModel:BindableBase
  4. {
  5. private string _value = "Hello MainWindowViewModel";
  6. public string Value {
  7. get { return _value; }
  8. set { SetProperty(ref _value, value); }
  9. }
  10. }
  11. }


在这个视图模型中,我们定义了一个 Value 属性,用于存储和提供数据给视图。
SetProperty 方法是 Prism 提供的一个辅助方法,用于简化属性的绑定和更新。
MainWindow.xaml

  1. <Window x:Class="LearningPrismUnityIoC.Views.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:LearningPrismUnityIoC"
  7. mc:Ignorable="d"
  8. xmlns:prism="http://prismlibrary.com/"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. Title="MainWindow" Height="450" Width="800">
  11. <Grid>
  12. <Button Content="按钮" Margin="0,0,672,364"></Button>
  13. <TextBlock Text="{Binding Value}" Margin="0,75,497,276"/>
  14. </Grid>
  15. </Window>


在视图中,我们通过 prism:ViewModelLocator.AutoWireViewModel="True" 实现了视图与 ViewModel 的自动绑定。TextBlockText 属性与 MainWindowViewModel 中的 Value 属性进行了绑定。

自动绑定机制的解析


Prism 提供的 ViewModelLocator 是一个强大的工具,它能够自动将视图与对应的 ViewModel 进行绑定。
具体来说,ViewModelLocator 会根据视图的命名约定在指定的命名空间中查找对应的 ViewModel 类。

例如,MainWindow 视图会自动查找 MainWindowViewModel 类作为其 ViewModel。这种自动绑定机制大大减少了样板代码,提高了开发效率。

数据更新与绑定的实现


MainWindowViewModel 中,Value 属性通过 SetProperty 方法实现了 INotifyPropertyChanged 接口,这意味着当 Value 属性的值发生变化时,与之绑定的视图元素会自动更新显示。
下面将进行展示:

自定义绑定机制


首先我们创建两个文件夹BobViewsBobModels,然后在BobViews下创建一个CCWindow窗体,在BobModels下创建一个需要进行绑定的CCModel类,目录结构如下所示:


CCWindow.xaml内容如下,就是绑定一个简单的Value值。

  1. <Window x:Class="LearningPrismUnityIoC.BobViews.CCWindow"
  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:LearningPrismUnityIoC.BobViews"
  7. mc:Ignorable="d"
  8. xmlns:prism="http://prismlibrary.com/"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. Title="CCWindow" Height="450" Width="800">
  11. <Grid>
  12. <TextBlock Text="{Binding Value}" />
  13. </Grid>
  14. </Window>


CCModel类中创建一个简单的Value属性:

  1. public class CCModel : BindableBase
  2. {
  3. private string _value = "Hello CCModel";
  4. public string Value
  5. {
  6. get { return _value; }
  7. set { SetProperty(ref _value, value); }
  8. }
  9. }


然后我们在App.xaml.cs中做一些修改,通过调用SetDefaultViewTypeToViewModelTypeResolver方法进行修改解释器,将试图解析到我们对应的模型。
下面完整的代码:

  1. public partial class App : PrismApplication
  2. {
  3. protected override Window CreateShell()
  4. {
  5. return Container.Resolve<CCWindow>();
  6. }
  7. protected override void RegisterTypes(IContainerRegistry containerRegistry)
  8. {
  9. }
  10. protected override void ConfigureViewModelLocator()
  11. {
  12. base.ConfigureViewModelLocator();
  13. ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(ViewToViewModelResolver);
  14. }
  15. /// <summary>
  16. ///
  17. /// </summary>
  18. /// <param name="type">需要进行匹配的试图模型</param>
  19. /// <returns></returns>
  20. /// <exception cref="NotImplementedException"></exception>
  21. private Type? ViewToViewModelResolver(Type type)
  22. {
  23. var viewName = type.FullName;
  24. var vmName = viewName.Replace(".BobViews.", ".BobModels.");
  25. // 如果是Window结尾,则我们需要截取字符串
  26. if (vmName.EndsWith("Window"))
  27. {
  28. vmName = vmName.Substring(0,vmName.Length - 6);
  29. }
  30. vmName += "Model";
  31. return Type.GetType(vmName);
  32. }
  33. }

临时绑定

  1. protected override void ConfigureViewModelLocator()
  2. {
  3. base.ConfigureViewModelLocator();
  4. //ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(ViewToViewModelResolver);
  5. ViewModelLocationProvider.Register<CCWindow, CCModel>();
  6. }


SetDefaultViewTypeToViewModelTypeResolver方法会对所有的解析都会产生影响,如果我们只是某个别的需要进行绑定,我们可以通过注册的方式来完成。举例:

属性注入


如果我们有其他的接口和属性,我们可以这样进行注入。

  1. [Dependency]
  2. public IDataBaseAccess _data { get; set; }

注意需要先注册IDataBaseAccess才能注入。

事件聚合器

IEventAggregator


IEventAggregator 是 Prism 框架中的一个接口,用于获取和管理事件。
它背后的实现类 EventAggregator 提供了发布/订阅多播的功能。
这意味着可以有多个发布者发起同一个事件,也可以有多个订阅者监听同一个事件。
这种机制允许组件之间通过事件进行通信,而无需直接引用彼此。
创建一个事件

  1. /// <summary>
  2. /// 事件创建
  3. /// </summary>
  4. public class TestEvent:PubSubEvent<object>
  5. {
  6. }


然后我们在Model中进行绑定事件方法,并且EventMessage方法进行处理,然后创建一个BtnCommand命令用于发布事件。

  1. public class CCModel : BindableBase
  2. {
  3. private string _value = "Hello CCModel";
  4. public string Value
  5. {
  6. get { return _value; }
  7. set { SetProperty(ref _value, value); }
  8. }
  9. public CCModel(IEventAggregator eventAggregator)
  10. {
  11. _eventAggregator = eventAggregator;
  12. // 创建一个事件
  13. _eventAggregator.GetEvent<TestEvent>().Subscribe(EventMessage);
  14. }
  15. private void EventMessage(object obj)
  16. {
  17. }
  18. IEventAggregator _eventAggregator;
  19. public ICommand BtnCommand
  20. {
  21. get => new DelegateCommand(() => {
  22. _eventAggregator.GetEvent<TestEvent>().Publish("Hello Test Event");
  23. });
  24. }
  25. }


窗体我们创建一个按钮进行绑定事件。

  1. <Window x:Class="LearningPrismUnityIoC.BobViews.CCWindow"
  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:LearningPrismUnityIoC.BobViews"
  7. mc:Ignorable="d"
  8. xmlns:prism="http://prismlibrary.com/"
  9. prism:ViewModelLocator.AutoWireViewModel="True"
  10. Title="CCWindow" Height="450" Width="800">
  11. <Grid>
  12. <TextBlock Text="{Binding Value}" />
  13. <Button Command="{Binding BtnCommand}" Content="测试事件" Margin="0,0,567,316"/>
  14. </Grid>
  15. </Window>


注册事件可以在多个地方进行注册,举例,我们在CCWindow.xaml.cs中进行注册,那么它会在我们点击之后两边都会进行执行:

  1. public partial class CCWindow : Window
  2. {
  3. public CCWindow(IEventAggregator eventAggregator)
  4. {
  5. eventAggregator.GetEvent<TestEvent>().Subscribe(EventMessage);
  6. InitializeComponent();
  7. }
  8. private void EventMessage(object obj)
  9. {
  10. }
  11. }


除此之外它还可以定义什么样的线程来执行这样的事件。
如下面代码所示:

  1. // 这里使用了UI线程执行
  2. _eventAggregator.GetEvent<TestEvent>().Subscribe(EventMessage,ThreadOption.UIThread);
  3. // 它支持三种线程
  4. public enum ThreadOption
  5. {
  6. // 发布线程
  7. PublisherThread,
  8. // UI渲染线程
  9. UIThread,
  10. // 后台线程
  11. BackgroundThread
  12. }


这里传第三个参数是keepSubscriberReferenceAlive,当设置为 true 时,PubSubEvent 会使用强引用来保留对订阅者的引用,避免垃圾回收机制回收订阅者对象。
当设置为 false 时,PubSubEvent 会使用弱引用来引用订阅者,这样订阅者对象可以在不需要时被垃圾回收。
默认情况下,keepSubscriberReferenceAlive 参数为 false,也就是说,默认情况下是使用弱引用。

  1. _eventAggregator.GetEvent<TestEvent>().Subscribe(EventMessage,ThreadOption.UIThread,false);

通过这些信息,我能够总结出这个参数的两个主要应用场景:
当订阅者需要长期保持订阅状态时(如全局模块的订阅),可以设置为 true,以确保订阅者不会被过早回收。
当订阅者仅在某个作用域内有效时(如视图模型在窗口关闭后不再需要订阅),通常设置为 false,以便订阅者可以被垃圾回收。

  1. _eventAggregator.GetEvent<TestEvent>().Subscribe(EventMessage,ThreadOption.UIThread,false, filter => filter.ToString().Contains(""));


第四个参数用于判断是否需要触发该事件。


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

评价

序列化和反序列化的应用

序列化:var jsonstr= JsonConvert.SerializeObject(result);//将字符串序列化为JSON对象 反序列化: API api = new API();...

.net core 为选项数据添加验证:避免错误配置的应用接收用户流量

.net core 为选项数据添加验证:避免错误配置的应用接收用户流量[TOC] 三种验证方法 直接注册验证函数实现 IValidate...

.netcore3.1 RabbitMq Routing的应用

.netcore3.1 RabbitMq Routing的应用[TOC] 在前面很多文章中都用到了routingKey参数但并没有细说它的用途。在这之前我可...

spring boot配置文件的应用

springboot实例注意参数引用是用$
这一世以无限游戏为使命!
排名
2
文章
657
粉丝
44
评论
93
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
欢迎加群交流技术