C# WPF的MVVM模式中使用消息的订阅和发布

By jerryxjr1220 at 2023-08-27 • 0人收藏 • 380人看过

C#的WPF开发中经常会遇到需要在不同窗口或者界面间传递消息,如果要严格遵循Model-View-ViewModel前后台分离的原则来开发的话,数据的跨界面传输就会比较麻烦,尤其涉及到多个不同界面间的相互交互。

PubSub库的订阅和发布可以比较方便的解决这个问题。

1. Model: 消息模型

using CommunityToolkit.Mvvm.ComponentModel;

namespace WpfPubSubDemo
{
    public partial class MessageModel: ObservableObject
    {
        [ObservableProperty]
        public string? sender;
        [ObservableProperty]
        public string message;
        public MessageModel()
        {
            Sender = null;
            Message = string.Empty;
        }
    }
}

2. View: 界面

2.1 主窗口MainWindow

Xaml

<Window x:Class="WpfPubSubDemo.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:local="clr-namespace:WpfPubSubDemo"
        d:DataContext="{d:DesignInstance Type=local:MainViewModel}"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Frame x:Name="PageFrame"></Frame>
        <TextBlock Grid.Column="1" HorizontalAlignment="Center" Margin="0,134,0,0" TextWrapping="Wrap" d:Text="TextBlock" Text="{Binding MsgModel.Message}" VerticalAlignment="Top"/>
        <Label Grid.Column="1" Content="订阅者" HorizontalAlignment="Center" Margin="0,95,0,0" VerticalAlignment="Top"/>

    </Grid>
</Window>

CS

using System.Windows;


namespace WpfPubSubDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainViewModel viewModel { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            viewModel = new MainViewModel();
            DataContext = viewModel;
            PageFrame.Navigate(new PageView());
        }
    }
}

2.2 子窗口PageView

Xaml

<Page x:Class="WpfPubSubDemo.PageView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:WpfPubSubDemo"
      d:DataContext="{d:DesignInstance Type=local:PageViewModel}"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="PageView">

    <Grid Background="#FFFEFEFE">
        <TextBox x:Name="MsgTextBox" HorizontalAlignment="Center" Margin="0,113,0,0" TextWrapping="Wrap" Text="{Binding Msg.Message, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
        <Button Content="发布" Command="{Binding PublishCommand}" HorizontalAlignment="Center" Margin="0,165,0,0" VerticalAlignment="Top"/>
        <Label Content="发布者" HorizontalAlignment="Center" Margin="0,73,0,0" VerticalAlignment="Top"/>

    </Grid>
</Page>

CS

using System.Windows.Controls;

namespace WpfPubSubDemo
{
    /// <summary>
    /// PageView.xaml 的交互逻辑
    /// </summary>
    public partial class PageView : Page
    {
        public PageViewModel pageViewModel;
        public PageView()
        {
            InitializeComponent();
            pageViewModel = new PageViewModel();
            DataContext = pageViewModel;
        }
    }
}

3. ViewModel:  逻辑调度

3.1 主窗体MainViewModel:

using CommunityToolkit.Mvvm.ComponentModel;
using PubSub;

namespace WpfPubSubDemo
{

    public partial class MainViewModel: ObservableObject
    {
        [ObservableProperty]
        public MessageModel msgModel;

        public Hub hub { get; set; }

        public MainViewModel()
        {
            MsgModel = new MessageModel();
            hub = Hub.Default;
            hub.Subscribe<MessageModel>((m) => MsgModel = m);

        }

    }

}

3.2 子窗口PageViewModel:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PubSub;


namespace WpfPubSubDemo
{
    public partial class PageViewModel: ObservableObject
    {
        [ObservableProperty]
        public MessageModel msg;

        public PageViewModel()
        {
            Msg = new MessageModel();
            Msg.Message = "Init";
        }

        [RelayCommand]
        public void Publish()
        {
            Hub.Default.Publish(Msg);
        }
    }
}


8 个回复 | 最后更新于 2023-08-30
2023-08-27   #1

如果在winform中我还是喜欢用Easy.MessageHub通过定义个全局静态类实现这个, 用的时候就和aardio里一样了. 主要是之前习惯了aar这种写法了.哈


2023-08-27   #2

回复#1 @admin :

WPF其实和WinForm非常不一样了,尤其是用了MVVM模式以后要做到完全的前后端分离,不通过消息的订阅和发布几乎就不可能在界面之间交互了。

其实CommunityToolkit.Mvvm是自带了Message消息的发布和订阅的,只不过需要做很多消息注册以及实现各种接口的操作,相对来说用起来比较麻烦。

PubSub这个库就挺傻瓜式的,Publish和Subscribe一共就2句就实现了消息的传递。

如果在主程序的MainViewModel中建一个类似全局变量的对象来存放共享数据也是可以的,只需要注册一个OnPropertyChangedEvent 事件,当共享数据发生变化时,同步发布出去,就可以通知到所有订阅了该消息的对象。而WPF的Binding表达式更简化了这个操作,直接可以绑定到共享数据的对象上。

2023-08-27   #3

定制了一个项目模板,包含WPF + CommunityToolkit.Mvvm + Behaviors + PubSub + HandyControls

都已经配置好了,直接导入VS模板即可

WpfApplicationBase.zip


2023-08-28   #4

上楼中的HandyControls是3.4.5版本,好像有些问题 - 在VS中的静态资源无法自动提示,但显示是正常的,用起来不是很爽。

降到3.4.0版本,更新了App.xaml的资源包

<Application x:Class="WpfApplicationBase.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplicationBase"
             xmlns:hc="https://handyorg.github.io/handycontrol"
             StartupUri="Views\MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
                <hc:Theme />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

现在可以正常提示了

无标题.png

更新后的模板文件

WpfApplicationBase.zip


2023-08-28   #5

HandyControl 还是比较符合现代扁平化设计的风格

无标题.png

2023-08-29   #6

多说一句,上面那个VS模板WpfApplicationBase.zip文件应当放到

C:\Users\你的电脑名称\Documents\Visual Studio 2022\Templates\ProjectTemplates

然后在VS中新建项目的时候就可以直接选择这个模板了,千万不要解压zip后去打开啊,那样是运行不了的

2023-08-29   #7

再分享一个WPF的无边框窗口式样模板

已实现了无边框窗口的拖拽、放缩等功能,直接导入模板即可。

WPFWindow.zip

捕获.PNG

2023-08-30   #8

根据上述模板开发

无标题.png

登录后方可回帖

登 录
信息栏
 私人小站

本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网


才仁机械


网站地图SiteMap

Loading...