WPF MvvMLight

一、MVVM概述

MVVM是Model-View-ViewModel的简写,主要目的是为了解耦视图(View)和模型(Model)。

MVVM结构如下:

相对于之前把逻辑结构写在Code Behind 的方式,MVVM模式几乎完全解耦了视图和逻辑业务的关系,通过数据绑定和命令绑定来处理UI属性及事件驱动;
同时,ViewModel中对属性的变更也会通知到View前端,让View前端实时更新。
二、MVVMLight概述
MVVMLight是一个实现MVVM模式的轻量级框架(相对于Prism),能够更好的帮助我们开发WPF 、Windows Phone、Windows 8、SilverLight相关项目。
MVVMLight的作者Laurent Bugnion 是个微软MVP,作者将代码开源到Codeplex上面:http://mvvmlight.codeplex.com/
MVVMLight的官网:http://www.mvvmlight.net/,上面有详细的介绍和视频文档,有兴趣可以了解下。
MvvMLight包含的动态库:

 

 

 
三、MVVMLight框架初探
通过NuGet安装MVVM Light 框架后,我们新建的Wpf项目中会自动生成一个ViewModel文件夹,里面有MainViewModel.cs和ViewModelLocator.cs两个文件。
下面我们就首先分析下这两个文件的内容:
MainViewModel.cs文件分析:
MainViewModel.cs文件中只有一个类MainViewModel,该类是主窗口MainWindow对应的ViewModel,继承自类ViewModelBase
ViewModelBase类又继承类ObservableObject,同时实现ICleanup接口
ObservableObject类实现INotifyPropertyChanged接口,用于通知属性的改变
由此分析,我们可以得出以下一般结论:
当我们定义一个自己的ViewModel时,一般让自定义ViewModel继承自ViewModelBase类,这样当ViewModel中属性发生变化的时候,就可以自动通知对应的VIew。
 
ViewModelLocator.cs文件分析:
ViewModelLocator.cs文件中只有一个ViewModelLocator类,类中包括一个构造函数、一个类型为MainViewModel的Main属性、以及一个静态的Cleanup函数。
 
 
复制代码
 public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            ////if (ViewModelBase.IsInDesignModeStatic)
            ////{
            ////    // Create design time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DesignDataService>();
            ////}
            ////else
            ////{
            ////    // Create run time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DataService>();
            ////}

            SimpleIoc.Default.Register<MainViewModel>(); //容器在这里注册了MainViewModel类
        }

        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();//容器获取MainViewModel类
            }
        }
        
        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
复制代码

在构造函数中,创建了一个SimpleIoc类型的单实例,用于注册ViewModel,然后用ServiceLocator对这个SimpleIoc类型的单实例进行包裹,方便统一管理。
观察App.xaml文件,我们会发现ViewModelLocator类被生成资源字典并加入到了全局资源,所以每次App初始化的时候,就会去初始化ViewModelLocator类。

实际上,他是一个很基本的视图模型注入器,在构造器中把使用到的ViewModel统一注册,并生成单一实例。然后使用属性把它暴露出来,每当我们访问属性的时候,就会返回相应的ViewModel实例。
 
复制代码
<Application x:Class="MvvmLightDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:local="clr-namespace:MvvmLightDemo" StartupUri="MainWindow.xaml" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
  <Application.Resources>
    <ResourceDictionary>
      <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:MvvmLightDemo.ViewModel" />
    </ResourceDictionary>
  </Application.Resources>
</Application>
复制代码

ViewModel文件分析:类继承了ViewModelBase

 public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.

                this.MyProperty = "Hello 123![单独]";
            }
            else
            {
                this.MyProperty = "你好 456!";
                // Code runs "for real"
            }
        }

        private string myVar;

        public string MyProperty
        {
            get { return myVar; }
            set { myVar = value; this.RaisePropertyChanged(); } //this.RaisePropertyChanged()触发界面更改
        }

        private int _value;

        public int Value
        {
            get { return _value; }
            set { Set<int>(ref _value, value); }//Set里面已经实现了RaisePropertyChanged
        }


    }

四、MVVMLight命令RelayCommand

在MVVM Light框架中,主要通过命令绑定来进行事件的处理。

 WPF中,命令是通过实现 ICommand 接口创建的。 ICommand 公开了两个方法(Execute 及 CanExecute)和一个事件(CanExecuteChanged)。

在MVVM Light框架中,RelayCommand类实现了ICommand 接口,用于完成命令绑定。

通过RelayCommand类的构造函数传入Action类型的Execute委托和Func<bool>类型的CanExecute委托,CanExecute委托用于表示当前命令是否可以执行,Execute委托则表示执行当前命令对应的方法。

通过命令绑定,解耦了View和ViewModel的行为交互,将视图的显示和业务逻辑分开。比如我们对界面上的某个按钮进行命令绑定,当点击按钮的时候,实际上进行操作是在对应的ViewModel下的所绑定的方法中执行的。

RelayCommand包含带参数跟不带参数的,带参数的就用泛型RelayCommand<T>对象

 

 

 

 

1、带一个参数的命令绑定

代码片段如下:

复制代码
<StackPanel>
            <GroupBox Header="带string类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserList:" VerticalContentAlignment="Center" FontSize="20" ></Label>
                        <Label Content="{Binding Path=UserList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="20" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserName:" VerticalContentAlignment="Center" FontSize="20" ></Label>
                        <TextBox Width="200" Name="tbUser"></TextBox>
                        <Button Content="AddUser" Command="{Binding AddUserCommand}" CommandParameter="{Binding ElementName=tbUser,Path=Text}"></Button>
                        <CheckBox Content="IsCanAdd" VerticalAlignment="Center" FontSize="16" IsChecked="{Binding IsCanAddUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox>
                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>
复制代码
复制代码
 private RelayCommand<string> addUserCommand;

        public RelayCommand<string> AddUserCommand
        {
            get
            {
                if (addUserCommand == null)
                {
                    addUserCommand = new RelayCommand<string>(AddUser, (string p) => { return IsCanAddUser; });
                }
                return addUserCommand;
            }
            set { addUserCommand = value; }
        }
        private void AddUser(string par)
        {
            UserList = UserList + "  " + par;
        }
复制代码

2、带多个参数的命令绑定

给命令传递多个参数,建议使用以下方式:

使用MultiBinding将多绑定的各个值转换成我们所需的对象或者实例模型,再传递给ViewModel中的命令。

代码片段如下:

复制代码
<Window x:Class="MvvmLightDemo1.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:MvvmLightDemo1"
        xmlns:cvt="clr-namespace:MvvmLightDemo1.Converter"
        xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        mc:Ignorable="d"
        Title="MVVMLIghtDemo1" Height="500" Width="700"  >
    <Window.Resources>
        <cvt:UserInfoConverter x:Key="userInfoConverter"></cvt:UserInfoConverter>
    </Window.Resources>
    <Window.DataContext>
        <Binding Path="Main" Source="{StaticResource Locator}"></Binding>
    </Window.DataContext>
    <StackPanel>
        <StackPanel>
            <GroupBox Header="带string类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserList:" VerticalContentAlignment="Center" FontSize="20" ></Label>
                        <Label Content="{Binding Path=UserList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="20" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserName:" VerticalContentAlignment="Center" FontSize="20" ></Label>
                        <TextBox Width="200" Name="tbUser"></TextBox>
                        <Button Content="AddUser" Command="{Binding AddUserCommand}" CommandParameter="{Binding ElementName=tbUser,Path=Text}"></Button>
                        <CheckBox Content="IsCanAdd" VerticalAlignment="Center" FontSize="16" IsChecked="{Binding IsCanAddUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox>
                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>

        <StackPanel>
            <GroupBox Header="带对象类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FF127C0D" Margin="2">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserName:" FontSize="16" ></Label>
                        <TextBox Width="200" Name="tbxUser" FontSize="16" />
                        <Label Content="Password:" FontSize="16" ></Label>
                        <TextBox Width="200" Name="tbxPwd" FontSize="16" />
                        <Button Content="AddUser" Command="{Binding AddUserCommandWithObjPar}">
                            <Button.CommandParameter>
                                <MultiBinding Converter="{StaticResource userInfoConverter}">
                                    <Binding ElementName="tbxUser" Path="Text"/>
                                    <Binding ElementName="tbxPwd" Path="Text"/>
                                </MultiBinding>
                            </Button.CommandParameter>
                            
                        </Button>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="Parameter:"  FontSize="16" ></Label>
                        <Label Content="{Binding ObjParameter}"  FontSize="16" ></Label>

                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>

        <StackPanel>
            <GroupBox Header="事件转命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2">
                <StackPanel>
                    <StackPanel>
                        <ListBox x:Name="lb" ItemsSource="{Binding ListBoxData}"  BorderThickness="0" SelectedIndex="{Binding SelectIndex}" >
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="SelectionChanged">
                                    <mvvm:EventToCommand Command="{Binding SelectionChangedCommand}"/>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                            <ListBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"/>
                                </ItemsPanelTemplate>
                            </ListBox.ItemsPanel>

                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <Border BorderBrush="AntiqueWhite" BorderThickness="1">
                                        <StackPanel Margin="2">

                                            <Image Source="{Binding Img}" Width="96" Height="96"/>
                                            <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/>
                                
                                       
                                    </StackPanel>
                                    </Border>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                        <Label Content="您选择的是:" FontSize="16" ></Label>
                        <Label Content="{Binding Path=SelResult,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="16" />
                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>

    </StackPanel>
</Window>
复制代码
复制代码
using MvvmLightDemo1.ViewModel;
using System;
using System.Linq;
using System.Windows.Data;

namespace MvvmLightDemo1.Converter
{
    public class UserInfoConverter: IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text)) && values.Count() == 2)
            {
                UserModel userModel = new UserModel() { UserName = values[0].ToString(), PassWord = values[1].ToString()};
                return userModel;
            }

            return null;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
复制代码
复制代码
   private RelayCommand<UserModel> addUserCommandWithObjPar;

        public RelayCommand<UserModel> AddUserCommandWithObjPar
        {
            get
            {
                if (addUserCommandWithObjPar == null)
                {
                    addUserCommandWithObjPar = new RelayCommand<UserModel>(AddUserWithObjPar);
                }
                return addUserCommandWithObjPar;
            }
            set { addUserCommandWithObjPar = value; }
        }
        private void AddUserWithObjPar(UserModel par)
        {
            ObjParameter = "UserName: "+ par.UserName + " Password: " + par.PassWord;
        }
复制代码
3、EventToCommand

在WPF中,并不是所有控件都有Command,例如TextBox,那么当文本改变,我们需要处理一些逻辑,这些逻辑在ViewModel中,没有Command如何绑定呢?

这个时候我们就用到EventToCommand,事件转命令,可以将一些事件例如TextChanged,Checked等事件转换成命令的方式。

接下来我们就以ListBox为例子,来看看具体的实例:

View代码:(这边声明了i特性和mvvm特性,一个是为了拥有触发器和行为附加属性的能力,当事件触发时,会去调用相应的命令,EventName代表触发的事件名称;一个是为了使用MVVMLight中 EventToCommand功能。)

这边就是当ListBox执行SelectionChanged事件的时候,会相应去执行ViewModel中 SelectionChangedCommand命令。

代码片段如下:

<Window x:Class="MvvmLightDemo1.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:MvvmLightDemo1"
        xmlns:cvt="clr-namespace:MvvmLightDemo1.Converter"
        xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        mc:Ignorable="d"
        Title="MVVMLIghtDemo1" Height="500" Width="700"  >
    <Window.Resources>
        <cvt:UserInfoConverter x:Key="userInfoConverter"></cvt:UserInfoConverter>
    </Window.Resources>
    <Window.DataContext>
        <Binding Path="Main" Source="{StaticResource Locator}"></Binding>
    </Window.DataContext>
    <StackPanel>
        <StackPanel>
            <GroupBox Header="带string类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserList:" VerticalContentAlignment="Center" FontSize="20" ></Label>
                        <Label Content="{Binding Path=UserList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="20" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserName:" VerticalContentAlignment="Center" FontSize="20" ></Label>
                        <TextBox Width="200" Name="tbUser"></TextBox>
                        <Button Content="AddUser" Command="{Binding AddUserCommand}" CommandParameter="{Binding ElementName=tbUser,Path=Text}"></Button>
                        <CheckBox Content="IsCanAdd" VerticalAlignment="Center" FontSize="16" IsChecked="{Binding IsCanAddUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox>
                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>

        <StackPanel>
            <GroupBox Header="带对象类型参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FF127C0D" Margin="2">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="UserName:" FontSize="16" ></Label>
                        <TextBox Width="200" Name="tbxUser" FontSize="16" />
                        <Label Content="Password:" FontSize="16" ></Label>
                        <TextBox Width="200" Name="tbxPwd" FontSize="16" />
                        <Button Content="AddUser" Command="{Binding AddUserCommandWithObjPar}">
                            <Button.CommandParameter>
                                <MultiBinding Converter="{StaticResource userInfoConverter}">
                                    <Binding ElementName="tbxUser" Path="Text"/>
                                    <Binding ElementName="tbxPwd" Path="Text"/>
                                </MultiBinding>
                            </Button.CommandParameter>
                            
                        </Button>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="Parameter:"  FontSize="16" ></Label>
                        <Label Content="{Binding ObjParameter}"  FontSize="16" ></Label>

                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>

        <StackPanel>
            <GroupBox Header="事件转命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FFCDAA0C" Margin="2">
                <StackPanel>
                    <StackPanel>
                        <ListBox x:Name="lb" ItemsSource="{Binding ListBoxData}"  BorderThickness="0" SelectedIndex="{Binding SelectIndex}" >
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="SelectionChanged">
                                    <mvvm:EventToCommand Command="{Binding SelectionChangedCommand}"/>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                            <ListBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"/>
                                </ItemsPanelTemplate>
                            </ListBox.ItemsPanel>

                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <Border BorderBrush="AntiqueWhite" BorderThickness="1">
                                        <StackPanel Margin="2">

                                            <Image Source="{Binding Img}" Width="96" Height="96"/>
                                            <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/>
                                
                                       
                                    </StackPanel>
                                    </Border>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                        <Label Content="您选择的是:" FontSize="16" ></Label>
                        <Label Content="{Binding Path=SelResult,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="16" />
                    </StackPanel>
                </StackPanel>
            </GroupBox>
        </StackPanel>
     <StackPanel>
            <GroupBox Header="带事件本身参数的命令" BorderBrush="#FF11519C" BorderThickness="1" FontSize="16" Foreground="#FF127C0D" Margin="2">

                <StackPanel Orientation="Horizontal">
                    <Button Content="拖拽上传文件" AllowDrop="True">
                        <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Drop">
                            <mvvm:EventToCommand PassEventArgsToCommand="True" Command="{Binding DropCommand}" />
                        </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </Button>
                    <Label Content="FilePath:"  FontSize="16" ></Label>
                    <Label Content="{Binding DraggedFilePath}"  FontSize="16" ></Label>

                </StackPanel>

            </GroupBox>
        </StackPanel>
</StackPanel> </Window>

五、MVVMLight消息Messenger

Messeger是信使的意思,顾名思义,他的目是用于View和ViewModel 以及 ViewModel和ViewModel 之间的消息通知和接收。Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message)实现,在这种情况下信息只能被传递如果接受者类型和目标参数类型匹配,message可以是任何简单或者复杂的对象,你可以用特定的消息类型或者创建你自己的类型继承自他们。

 

 Messenger首先需要注册一下,用来接收消息

Messenger.Default.Register<string>(this, (o) =>
{
MessageBox.Show("收到字符串数据:" + o);
});

Messenger.Default.Register<string>(this, (o) =>
{
MessageBox.Show("收到字符串数据:" + o);
},"token");//带令牌的注册

 

// 接口注册receiveDerivedMessagesToo为false,继承的对象发送将不能接收,为TRUE继承的对象发送可以接收
Messenger.Default.Register<IA>(this, false, (o) =>
{

});

比如Messanger.Default.Send<A>()//false,发送不能接收

Messanger.Default.Register<T>泛型方法,T代表指定类型,只有发送这个类型的消息才能被指定泛型注册的方法捕获到

消息发送:

Messenger.Default.Send("123");
Messenger.Default.Send("123", "token");//"token"消息令牌,如果带了令牌,只有注册了该令牌的消息才能接收到

Messenger.Default.Send<NotificationMessage>(new NotificationMessage(this, "123"));

Messenger.Default.Send(null, "token");//如果需要发送广播消息,消息内容为空

Message消息对象类型 说明

MessageBase  简单的消息类,携带可选的信息关于消息发布者的

GenericMessage<T>  泛型消息

NotificationMessage  用于发送一个string类型通知给接受者
NotificationMessage<T>
和上面一样是一个,且具有泛型功能

NotificationMessage 向接受者发送一个通知,允许接受者向发送者回传消息
NotificationMessageAction<T> NotificationMessage的泛型方式
DialogMessage 发送者(通常是View)显示对话,并且传递调用者得回传结果(用于回调),接受者可以选择怎样显示对话框,可以使是标准的MessageBox也可也是自定义弹出窗口
PropertyChangedMessage<T> 用于广播一个属性的改变在发送者里,和PropertyChanged事件有完全箱体内各的目的,但是是一种弱联系方式

DialogMessage发送代码:

//通过命令发送令牌信息
public ICommand TestMathCommand
{
get
{
return new RelayCommand(() =>
{
DialogMessage msg = new DialogMessage("提示正文", MessageBoxCallBack);
//设置弹出的MessageBox的类型,包含两个按钮,一个"确认", 一个"取消"
msg.Button = MessageBoxButton.OKCancel;
msg.Caption = "标题";
Messenger.Default.Send<DialogMessage>(msg);
});
}
}

private void MessageBoxCallBack(MessageBoxResult ret)
{
if (ret == MessageBoxResult.OK)
{
MessageBox.Show("弹窗点击了确认。继续执行。");
}
}
DialogMessage接收代码:

public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(this, dlgmsg =>
{
var res = MessageBox.Show(dlgmsg.Content, dlgmsg.Caption, dlgmsg.Button);
dlgmsg.Callback(res); //这句会此起ViewModel中的MessageBoxCallBack调用
});
}
DialogMessage包含了弹出框需要的全部信息,包括标题,正文和回调函数。在这个Demo中需要注意一下MessageBox是模态的,因此当执行到MessageBox.Show()时,执行流会卡住,等到用户点击"OK"惑"Cancel"按钮后,程序才会继续执行。

NotificationMessageAction发送代码:

//通过命令发送令牌信息
public ICommand TestMathCommand
{
get
{
return new RelayCommand(() =>
{
NotificationMessageAction<MessageBoxResult> msg = new NotificationMessageAction<MessageBoxResult>("主页面测试", (ret) =>
{
if (ret == MessageBoxResult.OK)
{
MessageBox.Show("弹窗点击了确认。继续执行。");
}
}
);

Messenger.Default.Send<NotificationMessageAction<MessageBoxResult>>(msg);
});
}
}
NotificationMessageAction接收代码:

public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<NotificationMessageAction<MessageBoxResult>>(this, (msg) =>
{
var ret = MessageBox.Show("内容", "标题", MessageBoxButton.OKCancel);
//msg.Notification内容是“主页面测试”
if (msg.Notification != null)
{
msg.Execute(ret);
}
}
);
}

如果ViewModel需要向View发送一个对象,可以使用 NotificationMessage<类型>的泛型版本

// PropertyChangedMessage属性消息,当属性更改会触发消息通知,属性需要把广播的参数设置为true

   比如:public string name

{

get

{

   return _name;

}

set

{

   set(ref _name,value,broadcast:true);

}

}
Messenger.Default.Register<PropertyChangedMessage<string>>(this, (o) =>
{
 if (o.PropertyName == "MyProperty")// 区别一下哪个属性进行了变化
 {

 }
});

六、MVVMLight DispatchHelper UI更新

在应用程序中,线程可以被看做是应用程序的一个较小的执行单位。每个应用程序都至少拥有一个线程,我们称为主线程。当调用和操作主线程的时候,该操作将动作添加到一个队列中,每个操作均按照将它们添加到队列中的顺序连续执行,但是可以通过为这些动作指定优先级来影响执行顺序,而负责管理此队列的对象称之为线程调度程序。

我们知道,WPF程序中,控件是属于UI线程的,如果试图在子线程中访问或者更新UI,就需要在子线程中通知主线程来处理UI, 通过向主线程的Dispatcher队列注册工作项,来通知UI线程更新结果。

Dispatcher提供两个注册工作项的方法:Invoke 和 BeginInvoke。

这两个方法均调度一个委托来执行。Invoke 是同步调用,也就是说,直到 UI 线程实际执行完该委托它才返回。BeginInvoke是异步的,将立即返回。

代码片段如下:

 this.Dispatcher.BeginInvoke((Action)delegate()
            {
                更新UI控件ing;
});

通常情况下,ViewModel 不从 DispatcherObject 继承,不能访问 Dispatcher 属性。这时候,我们需要使用DispatcherHelper 组件来更新UI。

实际上,该类所做的是将主线程的调度程序保存在静态属性中,并公开一些实用的方法,以便通过统一的方式访问。

为了实现正常功能,需要在主线程上初始化该类。

通常,在 MVVM Light 应用程序中,DispatcherHelper 可以在 App.xaml.cs 或者ViewModel的构造函数中进行初始化,App.xaml.cs 是定义应用程序启动类的文件。

在 WPF 中,该类一般是在 App 构造函数中进行初始化的。

DispatcherHelper组件初始化以后,DispatcherHelper 类的 UIDispatcher 属性包含对主线程的调度程序的引用。

但是一般很少直接使用该属性,虽然确实可以使用。通常我们会使用 CheckBeginInvokeOnUi 方法来更新UI。

 

 

 

 

 

七、MVVMLight  消息对话框

 

 首先实现IDialgService类,然后注册类,然后调用类显示消息对话框

注册对话框类:

  public class B : IA, IDialogService
    {
        public B()
        {
        }

        public void Show()
        {
            throw new NotImplementedException();
        }

        public Task ShowError(string message, string title, string buttonText, Action afterHideCallback)
        {
            throw new NotImplementedException();
        }

        public Task ShowError(Exception error, string title, string buttonText, Action afterHideCallback)
        {
            throw new NotImplementedException();
        }

        public Task ShowMessage(string message, string title)
        {
            MessageBox.Show(message, title);
            return null;
        }

        public Task ShowMessage(string message, string title, string buttonText, Action afterHideCallback)
        {
            throw new NotImplementedException();
        }

        public Task<bool> ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action<bool> afterHideCallback)
        {
            throw new NotImplementedException();
        }

        public Task ShowMessageBox(string message, string title)
        {
            throw new NotImplementedException();
        }
    }

注册类

 

 

 

// IDialogService的使用
SimpleIoc.Default.GetInstance<IDialogService>().ShowMessage("", "");

 

posted @ 2021-12-08 11:02  zyl2  阅读(1375)  评论(0编辑  收藏  举报