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("", "");

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM