当前位置: 首页 > news >正文

WPF MVVM进阶系列教程(四、ViewModel通信)

方式一、通过依赖注入直接调用ViewModel

在前面的文章中,我们介绍了使用DI容器。

在注入ViewModel时,将它的生命周期配置为单例,这样我们就可以在任意的ViewModel中进行互相调用。

这里我们创建一个Send窗口和一个Receive窗口。

Send窗口用于发送消息,Receive窗口用于接收消息。

Send.xaml

1 <Window>
2     <Grid>
3         <Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Send" Command="{Binding SendMessageCommand}"></Button>
4     </Grid>
5 </Window>

SendViewModel

这里我们将接收的ViewModel直接以参数传入,这样就可以直接操作Receive窗口中的列表。

 1 public class SendViewModel2 {3     public RelayCommand SendMessageCommand { get; private set; }4 5     protected ReceiveViewModel receiveViewModel;6 7     public SendViewModel(ReceiveViewModel receiveViewModel)8     {9         SendMessageCommand = new RelayCommand(SendMessage);
10 
11         this.receiveViewModel = receiveViewModel;
12     }
13 
14     private void SendMessage()
15     {
16         DateTimeMessage dateTimeMessage = new DateTimeMessage();
17         dateTimeMessage.DatetimeNow = DateTime.Now.ToString();
18         receiveViewModel.AddDateTimeMessage(dateTimeMessage);
19     }
20 }

Receive.xaml

1 <Window>
2     <Grid>
3         <ListBox ItemsSource="{Binding MessageList}"></ListBox>
4     </Grid>
5 </Window>

ReceiveViewModel

 1  public class ReceiveViewModel : INotifyPropertyChanged2  {3      private ObservableCollection<string> messageList = new ObservableCollection<string>();4 5      public ObservableCollection<string> MessageList6      {7          get8          {9              return messageList;
10          }
11 
12          set
13          {
14              messageList = value;
15              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MessageList"));
16          }
17      }
18      public event PropertyChangedEventHandler PropertyChanged;
19 
20      public ReceiveViewModel()
21      {
22          
23      }
24 
25 
26      public void AddDateTimeMessage(DateTimeMessage message)
27      {
28          messageList.Add(message.DatetimeNow);
29      }
30  }

然后我们配置DI容器

App.xaml.cs

 1 public partial class App : Application2 {3     public static IUnityContainer Container { get; private set; }4 5     protected override void OnStartup(StartupEventArgs e)6     {7         base.OnStartup(e);8 9         Container = new UnityContainer();
10         RegisterTypes(Container);
11 
12         Receive receive = new Receive();
13         receive.Show();
14     }
15 
16     private void RegisterTypes(IUnityContainer container)
17     {
18         container.RegisterType<SendViewModel>();
19         container.RegisterType<ReceiveViewModel>(new ContainerControlledLifetimeManager());
20     }
21 }

最后再通过DI容器获取ViewModel并绑定到DataContext

以Receive窗口为例

Receive.xaml.cs

1   public partial class Receive : Window
2   {
3       public Receive()
4       {
5           InitializeComponent();
6 
7           this.DataContext = App.Container.Resolve<ReceiveViewModel>();
8       }
9   }

方式二、通过回调

以前我们在使用MVVMLight包时,里面有一个Messenger类,它可以在ViewModel中订阅消息,当在其它位置发送这个消息时,订阅的ViewModel就可以接收到这个消息并进行处理。

MVVMLight示例如下:

ReceiveViewModel(创建订阅)

 1 using GalaSoft.MvvmLight;2 using GalaSoft.MvvmLight.Messaging;3 4 public class ReceiveViewModel : ViewModelBase5 {6     public ReceiverViewModel()7     {8         // 注册消息:指定消息类型、接收者(通常是自身)、处理方法9         Messenger.Default.Register<UserMessage>(this, OnUserMessageReceived);
10     }
11 
12     // 消息处理方法
13     private void OnUserMessageReceived(UserMessage message)
14     {
15         // 处理接收到的消息数据
16         var userId = message.UserId;
17         var userName = message.UserName;
18     }
19 }

SendViewModel(发送)

 1 using GalaSoft.MvvmLight;2 using GalaSoft.MvvmLight.Command;3 using GalaSoft.MvvmLight.Messaging;4 5 public class SenderViewModel : ViewModelBase6 {7     public RelayCommand SendMessageCommand { get; private set; }8 9     public SenderViewModel()
10     {
11         SendMessageCommand = new RelayCommand(SendMessage);
12     }
13 
14     private void SendMessage()
15     {
16         // 创建消息实例并设置数据
17         var message = new UserMessage 
18         { 
19             UserId = 1001, 
20             UserName = "张三" 
21         };
22 
23         // 发送消息
24         Messenger.Default.Send(message);
25     }
26 }

它这里内部的原理并不是非常复杂,就是利用回调的机制。

核心步骤如下:

1、创建一个字典类型,以类型名称(消息对象)作为Key,回调列表作为值

2、注册时,根据类型名称,将回调添加到列表中

3、发送时,循环列表判断是否注册了对应类型(消息对象),如果有,调用回调函数。

接下来我们自己实现一个简单的Messenger类,重在讲解原理。

public class Messager
{private static object obj = new object();private static Messager instance;private Dictionary<Type, List<GenericAction>> recipientsActions = new Dictionary<Type, List<GenericAction>>();//单例public static Messager Instance{get{if (instance == null){lock (obj){if (instance == null)instance = new Messager();}}return instance;}}//注册public void Register<TMessage>(Action<TMessage> action){var messageType = typeof(TMessage);List<GenericAction> list;if (!recipientsActions.ContainsKey(messageType)){list = new List<GenericAction>();recipientsActions.Add(messageType, list);}else{list = recipientsActions[messageType];}//将回调放到列表中list.Add(new GenericAction<TMessage>(action));}public void Send<TMessage>(TMessage message){var messageType = typeof(TMessage);List<GenericAction> list = new List<GenericAction>();if (recipientsActions.ContainsKey(messageType)){//获取当前消息对应的回调列表list = recipientsActions[messageType];}//循环回调列表并调用foreach (GenericAction<TMessage> action in list){action.Invoke(message);}}
}

ReceiveViewModel进行注册

 1  public class ReceiveViewModel : INotifyPropertyChanged2  {3      private ObservableCollection<string> messageList = new ObservableCollection<string>();4 5      public ObservableCollection<string> MessageList6      {7          get8          {9              return messageList;
10          }
11 
12          set
13          {
14              messageList = value;
15              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MessageList"));
16          }
17      }
18      public event PropertyChangedEventHandler PropertyChanged;
19 
20      public ReceiveViewModel()
21      {
22          //注册消息
23          Messager.Messager.Instance.Register<DateTimeMessage>(ReceiveDateTimeMessage);
24      }
25 
26      private void ReceiveDateTimeMessage(DateTimeMessage message)
27      {
28          messageList.Add(message.DatetimeNow);
29      }
30  }

SendViewModel中发送消息

 1 public class SendViewModel2 {3     public RelayCommand SendMessageCommand { get; private set; }4 5     public SendViewModel()6     {7         SendMessageCommand = new RelayCommand(SendMessage);8     }9 
10     private void SendMessage()
11     {
12         DateTimeMessage dateTimeMessage = new DateTimeMessage();
13         dateTimeMessage.DatetimeNow = DateTime.Now.ToString();
14         //发送消息
15         Messager.Messager.Instance.Send<DateTimeMessage>(dateTimeMessage);
16     }
17 }

完整代码可以参考文末链接

方式三、使用三方包提供的Messenger

注意:方式二重在了解原理,除非有特殊要求,尽量还是使用软件包提供的Messenger类,功能会更加完整和稳定。

这里我们以CommunityToolkit.MVVM包中的WeakReferenceMessenger类进行演示。

使用CommunityToolkit.MVVM包时,可通知的对象一般都会继承自ObservableRecipient类型,

这个类型的内部会注入一个WeakReferenceMessenger对象,所以使用起来就比较方便。

ReceiveViewModel

 1 public class ReceiveViewModel : ObservableRecipient2 {3     private ObservableCollection<string> messageList = new ObservableCollection<string>();4 5     public ObservableCollection<string> MessageList6     {7         get8         {9             return messageList;
10         }
11 
12         set
13         {
14             SetProperty(ref messageList, value);
15         }
16     }
17 
18     public ReceiveViewModel()
19     {
20         //注册
21         this.Messenger.Register<ReceiveViewModel, DateTimeMessage>(this, OnReceiveDateTimeChangedMessage);
22     }
23 
24     //接收到消息时的回调函数
25     private void OnReceiveDateTimeChangedMessage(ReceiveViewModel recipient, DateTimeMessage message)
26     {
27         messageList.Add(message.Value.DatetimeNow);
28     }
29 }

SendViewModel

 1 public class SendViewModel: ObservableRecipient2 {3     public RelayCommand SendMessageCommand { get; private set; }4 5     public SendViewModel()6     {7         SendMessageCommand = new RelayCommand(SendMessage);8     }9 
10     private void SendMessage()
11     {
12         //发送消息
13         this.Messenger.Send<DateTimeMessage>(new DateTimeMessage(new DateTimeDisplay() { DatetimeNow = DateTime.Now.ToString() }));
14     }
15 }

示例代码

https://github.com/zhaotianff/WPF-MVVM-Beginner/tree/main/10_ViewModelCommunication

http://www.dtcms.com/a/340724.html

相关文章:

  • std::map 的插入元素方式
  • 下拉组件Tag支持自定义背景颜色,图片组支持设置刷新频率,DataEase开源BI工具v2.10.12 LTS版本发布
  • iOS 应用上架常见问题与解决方案,多工具组合的实战经验
  • 深入解析RAGFlow六阶段架构
  • iOS 应用迭代与上架节奏管理 从测试包到正式发布的全流程实践
  • 操作系统:资源竞争或者同步问题;锁、信号量等机制
  • Mac 上安装并使用 frpc(FRP 内网穿透客户端)指南
  • MacBook Pro M1升级Burp Suite2025.8
  • Mac电脑上虚拟机共享文件夹权限问题
  • 数据挖掘笔记:点到线段的距离计算
  • 5.3 包管理工具 npm yarn pnpm 对比
  • AI与BI的协同:未来企业数据分析的趋势
  • 【考研408数据结构-06】 树与二叉树(上):遍历算法全解析
  • 【考研408数据结构-07】 树与二叉树(下):特殊树结构与应用
  • HTTPS协议与HTTP协议的区别
  • Web前端调试与性能优化,Charles抓包工具的高效应用
  • 计算机视觉(二)------OpenCV图像视频操作进阶:从原理到实战
  • vscode连接docker
  • 【网络运维】Linux:正则表达式
  • Gin自定义Error中间件
  • 【C++】--指针与引用深入解析和对比
  • Gin传参和接收参数的方式
  • K8S-Secret资源对象
  • 如何代开VSCode的settigns.json文件
  • 【运维】githubvercel学习使用
  • 数据结构--2:ArrayList与顺序表
  • 【机器学习深度学习】AI大模型高并发挑战:用户负载部署策略
  • 26_基于深度学习的茶叶等级检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 【JavaEE】多线程 -- CAS机制(比较并交换)
  • iPhone17系列超全准确预告