WPF之View、ViewModel之间的相互交互(一)

 MVVM的目标之一就是为了解耦View和ViewModel。View负责视图展示,ViewModel负责业务逻辑处理,尽量保证 View.xaml.cs中的简洁,不包含复杂的业务逻辑代码。

但是在实际情况中是View和ViewModel之间的交互方式还是比较复杂的,View和ViewModel的分离并不是界定的那么清晰。

比如以下两种场景:

      1、如果需要某张视图页面弹出对话框、弹出子窗体、处理界面元素,播放动画等。如果这些操作都放在ViewModel中,就会导致ViewModel还是要去处理View级别的元素,造成View和ViewModel的依赖。

最好的办法就是ViewModel通知View应该做什么,而View监听接收到命令,并去处理这些界面需要处理的事情。

      2、ViewModel和ViewModel之间也需要通过消息传递来完成一些交互。 

      而MVVM Light  的 Messenger类,提供了解决了上述两个问题的能力:

Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message) 实现,

在这种情况下信息只能被传递如果接受者类型和目标参数类型匹配,message可以是任何简单或者复杂的对象,你可以用特定的消息类型或者创建你自己的类型继承自他们。

Messager类的主要交互模式就是信息接受和发送(可以理解为“发布消息服务”和“订阅消息服务”),是不是想到观察者模式了,哈哈哈。

MVVM Light Messenger 旨在通过简单的设计模式来精简此场景:任何对象都可以是接收端;任何对象都可以是发送端;任何对象都可以是消息。

1、View和ViewModel之间的消息交互

在View和ViewModel中进行消息器注册,相当于订阅服务。包含消息标志、消息参数和消息执行方法。如下:

消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致

执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。

首先我们先熟悉一下基本的流程:

ViewModel代码: 

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MessengerTest.ViewModel
{
    public class MessengerRegisterForVViewModel:ViewModelBase
    {
        public MessengerRegisterForVViewModel()
        {

        }

        #region 命令
        private RelayCommand sendCommand;
        /// <summary>
        /// 发送命令
        /// </summary>
        public RelayCommand SendCommand
        {
            get
            {
                if (sendCommand == null)
                {
                    sendCommand = new RelayCommand(() => ExcuteSendCommand());
                }
                return sendCommand;
            }
            set
            {
                sendCommand = value;
            }
        }
        #endregion

       private void ExcuteSendCommand()
        {
            Messenger.Default.Send<String>("ViewModel通知View弹出消息框", "ViewAlert");
        }
    }
}

View.xaml.cs 代码如下 

using GalaSoft.MvvmLight.Messaging;
using MessengerTest.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace MessengerTest
{
    /// <summary>
    /// MessagerForView.xaml 的交互逻辑
    /// </summary>
    public partial class MessagerForView : Window
    {
        public MessagerForView()
        {
            InitializeComponent();

            ///消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,
            ///并执行相应的处理,
            ///所以Sender那边的token要保持一致
            ///执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,
            ///注意这边是支持泛型能力的,所以传递参数很方便。
            Messenger.Default.Register<String>(this, "ViewAlert", ShowReceiveInfo);
            this.DataContext = new MessengerRegisterForVViewModel();
            ///卸载当前(this)对象注册的所有MVVMLight消息
            this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
        }

        /// <summary>
        /// 接收到消息后的后续工作:根据返回来的信息弹出消息框
        /// </summary>
        /// <param name="msg"></param>
        private void ShowReceiveInfo(String msg)
        {
            MessageBox.Show(msg);
        }
    }
}

View.xaml代码:

<Window x:Class="MessengerTest.MessagerForView"
        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:MessengerTest"
        mc:Ignorable="d"
        Title="MessagerForView" Height="300" Width="300">
    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top">
            <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="115,115,0,0">
                <Button Content="弹出对话框" Command="{Binding SendCommand}">
                </Button>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

结果如下:

   

2、ViewModel和ViewModel之间的消息交互,ViewModel和ViewModel在很多种场景下也需要通过消息传递来完成一些交互。

比如我打开了两个视图,一个视图是用户信息列表,一个视图是用户信息添加页面,如果想要达到添加信息之后,用户信息列表视图实时刷新,用消息通知无疑是一个很棒的体验。

我们来模拟一下:

首先,我们还是先看一下流程

 MessengerSenderViewModel代码:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MessengerInfo.ViewModel
{
    public class MessengerSenderViewModel : ViewModelBase
    {
        public MessengerSenderViewModel()
        {

        }

        #region 属性
        private String sendInfo;
        /// <summary>
        /// 发送消息
        /// </summary>
        public String SendInfo
        {
            get { return sendInfo; }
            set { sendInfo = value; RaisePropertyChanged(() => SendInfo); }
        }

        #endregion

        #region 命令

        private RelayCommand sendCommand;
        /// <summary>
        /// 发送命令
        /// </summary>
        public RelayCommand SendCommand
        {
            get
            {
                if (sendCommand == null)
                    sendCommand = new RelayCommand(() => ExcuteSendCommand());
                return sendCommand;

            }
            set { sendCommand = value; }
        }

        private void ExcuteSendCommand()
        {
            Messenger.Default.Send<String>(SendInfo, "Message");
        }

        #endregion
    }
}

 MessengerSenderView代码:

using MessengerInfo.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace MessengerInfo
{
    /// <summary>
    /// MessengerSenderView.xaml 的交互逻辑
    /// </summary>
    public partial class MessengerSenderView : Window
    {
        public MessengerSenderView()
        {
            InitializeComponent();
            this.DataContext = new MessengerSenderViewModel();
        }
    }
}
<Window x:Class="MessengerInfo.MessengerSenderView"
        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:MessengerInfo"
        mc:Ignorable="d"
        Title="MessengerSenderView" Height="300" Width="300">
    <Grid>
        <StackPanel Orientation="Horizontal" Margin="10,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" >
            <TextBox Text="{Binding SendInfo}" Width="150" ></TextBox>
            <Button Command="{Binding SendCommand}" Content="发送消息"  Margin="15,0,0,0"></Button>
        </StackPanel>
    </Grid>
</Window>

MessengerRegisterViewModel 代码

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MessengerInfo.ViewModel
{
    public class MessengerRegisterViewModel : ViewModelBase
    {
        public MessengerRegisterViewModel()
        {
            ///Messenger:信使
            ///Recipient:收件人
            Messenger.Default.Register<String>(this, "Message", ShowReceiveInfo);
        }


        #region 属性

        private String receiveInfo;
        /// <summary>
        /// 接收到信使传递过来的值
        /// </summary>
        public String ReceiveInfo
        {
            get { return receiveInfo; }
            set { receiveInfo = value; RaisePropertyChanged(() => ReceiveInfo); }
        }

        #endregion


        #region 启动新窗口

        private RelayCommand showSenderWindow;

        public RelayCommand ShowSenderWindow
        {
            get
            {
                if (showSenderWindow == null)
                    showSenderWindow = new RelayCommand(() => ExcuteShowSenderWindow());
                return showSenderWindow;

            }
            set { showSenderWindow = value; }
        }

        private void ExcuteShowSenderWindow()
        {
            MessengerSenderView sender = new MessengerSenderView();
            sender.Show();
        }

        #endregion


        #region 辅助函数
        /// <summary>
        /// 显示收件的信息
        /// </summary>
        /// <param name="msg"></param>
        private void ShowReceiveInfo(String msg)
        {
            ReceiveInfo += msg + "\n";
        }
        #endregion
    }
}

MessengerRegisterView 代码 

using GalaSoft.MvvmLight.Messaging;
using MessengerInfo.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace MessengerInfo
{
    /// <summary>
    /// MessengerRegisterView.xaml 的交互逻辑
    /// </summary>
    public partial class MessengerRegisterView : Window
    {
        public MessengerRegisterView()
        {
            InitializeComponent();

            this.DataContext = new MessengerRegisterViewModel();
            //卸载当前(this)对象注册的所有MVVMLight消息
            this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
        }
    }
}
<Window x:Class="MessengerInfo.MessengerRegisterView"
        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:MessengerInfo"
        mc:Ignorable="d"
        Title="MessengerRegisterView" Height="300" Width="300">
    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" >
            <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="15,15,0,0" >
                <TextBlock Text="接收消息:" ></TextBlock>
                <TextBlock Text="{Binding ReceiveInfo}" FontSize="18" TextWrapping="Wrap" ></TextBlock>
            </StackPanel>

            <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"  Margin="15,15,0,0" >
                <Button Content="打开子窗口" Command="{Binding ShowSenderWindow}" ></Button>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

结果如下:

 

 

 

 

 

 

  • 12
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WPF(Windows Presentation Foundation)是一个用于创建 Windows 应用程序的框架,而 Prism 是一个基于 WPF 的框架,它提供了一组设计模式和工具,用于帮助开发人员创建可扩展、可重用和易于维护的应用程序。 在 Prism 中,ViewModel 是一个重要的组件,它的作用是将 View(即用户界面)与 Model(即数据和业务逻辑)解耦,使得两者可以独立进行开发和测试。 在 Prism 中,ViewModel 通常是一个简单的类,它实现了 INotifyPropertyChanged 接口,用于实现数据绑定。ViewModel 还包含了一些命令(Command),用于处理用户交互事件,比如按钮点击、菜单选择等。ViewModel 还可以使用服务(Service)来访问 Model,以获取或操作数据。 下面是一个简单的 ViewModel 的示例: ```csharp public class MainViewModel : BindableBase { private readonly IMyService _myService; private string _name; public string Name { get { return _name; } set { SetProperty(ref _name, value); } } private ICommand _helloCommand; public ICommand HelloCommand { get { if (_helloCommand == null) { _helloCommand = new DelegateCommand(ExecuteHelloCommand); } return _helloCommand; } } public MainViewModel(IMyService myService) { _myService = myService; } private void ExecuteHelloCommand() { string message = _myService.GetMessage(Name); MessageBox.Show(message); } } ``` 在上面的代码中,MainViewModel 包含了一个字符串属性 Name 和一个 ICommand 属性 HelloCommand。Name 属性用于将用户界面中的文本框绑定到 ViewModel 中,而 HelloCommand 则表示用户点击“Hello”按钮时要执行的命令。HelloCommand 的实现是通过调用一个服务(IMyService)来获取一条消息,然后通过 MessageBox 显示出来。 需要注意的是,MainViewModel 的构造函数中注入了一个 IMyService 接口,这个接口封装了对数据和业务逻辑的访问,使得 ViewModel 可以与具体的实现解耦。这种依赖注入的方式可以使得应用程序的组件更加松散耦合,更加易于维护和测试。 总之,ViewModelWPF Prism 中非常重要的一个组件,它用于实现视图与模型之间的解耦和交互。开发人员可以使用 ViewModel 实现数据绑定、命令处理、服务访问等功能,从而创建可扩展、可重用和易于维护的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值