WPF中MVVM模式的理解與實(shí)現(xiàn)
MVVM模式的介紹
MVVM(Model-View-ViewModel)是一種設(shè)計(jì)模式,特別適用于WPF(Windows Presentation Foundation)等XAML-based的應(yīng)用程序開發(fā)。MVVM模式主要包含三個部分:Model(模型)、View(視圖)和ViewModel(視圖模型)。
- Model(模型):模型代表的是業(yè)務(wù)邏輯和數(shù)據(jù)。它包含了應(yīng)用程序中用于處理的核心數(shù)據(jù)對象。模型通常包含業(yè)務(wù)規(guī)則、數(shù)據(jù)訪問和存儲邏輯。
- View(視圖):視圖是用戶看到和與之交互的界面。在WPF中,視圖通常由XAML定義,并且包含各種用戶界面元素,如按鈕、文本框、列表等。
- ViewModel(視圖模型):視圖模型是視圖的抽象,它包含視圖所需的所有數(shù)據(jù)和命令。視圖模型通過實(shí)現(xiàn)
INotifyPropertyChanged接口和使用ICommand對象,將視圖的狀態(tài)和行為抽象化,從而實(shí)現(xiàn)了視圖和模型的解耦。
MVVM模式的主要優(yōu)點(diǎn)是分離了視圖和模型,使得視圖和業(yè)務(wù)邏輯之間的依賴性降低,提高了代碼的可維護(hù)性和可測試性。此外,通過數(shù)據(jù)綁定和命令綁定,MVVM模式可以減少大量的樣板代碼,使得代碼更加簡潔和易于理解。

不使用MVVM模式的例子
要真正理解為什么要使用MVVM,使用MVVM有什么好處,肯定要與不使用MVVM的情況進(jìn)行對比。在Winform中我們使用了事件驅(qū)動編程,同樣在WPF中我們也可以使用事件驅(qū)動編程。
Windows Forms(WinForms)是一種基于事件驅(qū)動的圖形用戶界面(GUI)框架。在WinForms中,用戶與應(yīng)用程序的交互主要通過事件來驅(qū)動。
事件驅(qū)動編程是一種編程范式,其中程序的執(zhí)行流程由外部事件(如用戶操作或系統(tǒng)事件)決定。在WinForms中,事件可以是用戶的各種操作,如點(diǎn)擊按鈕、選擇菜單項(xiàng)、輸入文本等,也可以是系統(tǒng)的事件,如窗口加載、大小改變等。
當(dāng)一個事件發(fā)生時(shí),會觸發(fā)與之關(guān)聯(lián)的事件處理器(Event Handler)。事件處理器是一個函數(shù)或方法,用于響應(yīng)特定的事件。例如,當(dāng)用戶點(diǎn)擊一個按鈕時(shí),可以觸發(fā)一個事件處理器,執(zhí)行一些特定的操作。
在WinForms中,你可以為各種控件添加事件處理器,以響應(yīng)用戶的操作。這種事件驅(qū)動的方式使得你可以很容易地創(chuàng)建交互式的GUI應(yīng)用程序,而無需關(guān)心程序的執(zhí)行流程。
事件驅(qū)動的簡圖如下圖所示:

- 事件源(Event Source):事件源是產(chǎn)生事件的對象。在WinForms中,事件源通常是用戶界面元素,如按鈕、文本框、菜單項(xiàng)等。當(dāng)用戶與這些元素進(jìn)行交互(如點(diǎn)擊按鈕、輸入文本等)時(shí),這些元素就會產(chǎn)生相應(yīng)的事件。
- 事件(Event):事件是由事件源產(chǎn)生的一個信號,表示某種特定的事情已經(jīng)發(fā)生。例如,當(dāng)用戶點(diǎn)擊一個按鈕時(shí),按鈕就會產(chǎn)生一個Click事件。事件通常包含一些關(guān)于該事件的信息,例如事件發(fā)生的時(shí)間、事件的源對象等。
- 事件處理器(Event Handler):事件處理器是一個函數(shù)或方法,用于響應(yīng)特定的事件。當(dāng)一個事件發(fā)生時(shí),與該事件關(guān)聯(lián)的事件處理器就會被調(diào)用。在事件處理器中,你可以編寫代碼來定義當(dāng)事件發(fā)生時(shí)應(yīng)該執(zhí)行的操作。例如,你可以在按鈕的Click事件處理器中編寫代碼,定義當(dāng)用戶點(diǎn)擊按鈕時(shí)應(yīng)該執(zhí)行的操作。
現(xiàn)在我們通過一個例子在WPF中使用事件驅(qū)動編程。
首先看一下我們的示例xaml頁面:
<Window x:Class="WPF_MVVM_Pattern.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:WPF_MVVM_Pattern"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
Loaded="Window_Loaded">
<StackPanel>
<ToolBar>
<Label Content="姓名:"></Label>
<TextBox x:Name="nameTextBox" Width="50"></TextBox>
<Label Content="郵箱:"></Label>
<TextBox x:Name="emailTextBox" Width="100"></TextBox>
<Button Content="添加"
Click="AddUser"></Button>
</ToolBar>
<StackPanel>
<DataGrid x:Name="dataGrid1"></DataGrid>
</StackPanel>
</StackPanel>
</Window>

使用了兩個事件,分別是窗體加載事件:
Loaded="Window_Loaded"
與button點(diǎn)擊事件:
<Button Content="添加"
Click="AddUser"></Button>
實(shí)現(xiàn)該操作與兩個類有關(guān):
public class User
{
public string? Name { get; set; }
public string? Email { get; set; }
}
public static class UserManager
{
public static ObservableCollection<User> DataBaseUsers = new ObservableCollection<User>()
{
new User() { Name = "小王", Email = "123@qq.com" },
new User() { Name = "小紅", Email = "456@qq.com" },
new User() { Name = "小五", Email = "789@qq.com" }
};
public static ObservableCollection<User> GetUsers()
{
return DataBaseUsers;
}
public static void AddUser(User user)
{
DataBaseUsers.Add(user);
}
}
窗體加載事件處理程序:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
dataGrid1.ItemsSource = UserManager.GetUsers();
}
"添加"按鈕點(diǎn)擊事件處理程序:
private void AddUser(object sender, RoutedEventArgs e)
{
User user = new User();
user.Name = nameTextBox.Text;
user.Email = emailTextBox.Text;
UserManager.AddUser(user);
MessageBox.Show("成功添加用戶!");
}
實(shí)現(xiàn)的效果如下所示:

使用MVVM的例子
剛剛我們使用的是事件驅(qū)動編程,我們在winform開發(fā)中經(jīng)常這樣干。對于一些小項(xiàng)目或者demo程序這樣做很方便,但是如果業(yè)務(wù)邏輯很多,這樣做就不好維護(hù),因?yàn)閁I與業(yè)務(wù)邏輯嚴(yán)重耦合了。
我們經(jīng)常在cs文件中使用xaml中的元素,也就是經(jīng)常在cs中引用xaml中的元素,如下所示:

在C#代碼文件中直接引用XAML元素,會導(dǎo)致代碼與界面元素之間的耦合度增加,這是一種不良的編程實(shí)踐。以下是這種做法的一些潛在問題:
- 耦合度高:代碼與界面元素緊密耦合,這使得代碼更難以維護(hù)和重用。如果你更改了界面元素(例如更改了元素的名稱或類型),你可能需要修改引用這個元素的所有代碼。
- 測試?yán)щy:由于代碼直接依賴于界面元素,這使得單元測試變得困難。你可能需要創(chuàng)建一個界面元素的實(shí)例,或者使用復(fù)雜的模擬技術(shù),才能測試這些代碼。
- 違反MVVM模式:在WPF中,推薦使用MVVM(Model-View-ViewModel)模式來組織代碼。在MVVM模式中,視圖(View)和模型(Model)之間的交互是通過視圖模型(ViewModel)來進(jìn)行的,而不是直接在代碼中引用界面元素。
開始使用MVVM模式
RelayCommand
首先新建一個Commands文件夾,新建一個RelayComand類:

RelayCommand如下:
public class RelayCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
private Action<object> _Excute { get; set; }
private Predicate<object> _CanExcute { get;set; }
public RelayCommand(Action<object> ExcuteMethod, Predicate<object> CanExcuteMethod)
{
_Excute = ExcuteMethod;
_CanExcute = CanExcuteMethod;
}
public bool CanExecute(object? parameter)
{
return _CanExcute(parameter);
}
public void Execute(object? parameter)
{
_Excute(parameter);
}
}
RelayCommand實(shí)現(xiàn)了ICommand接口。
先來介紹一下ICommand接口。
ICommand
在WPF(Windows Presentation Foundation)中,ICommand是一個接口,它定義了一種機(jī)制,用于在用戶界面(UI)中處理事件,這種機(jī)制與用戶界面的具體行為進(jìn)行了解耦。這是實(shí)現(xiàn)MVVM(Model-View-ViewModel)設(shè)計(jì)模式的關(guān)鍵部分。
ICommand接口包含兩個方法和一個事件:
Execute(object parameter):當(dāng)調(diào)用此命令時(shí),應(yīng)執(zhí)行的操作。CanExecute(object parameter):如果可以執(zhí)行Execute方法,則返回true;否則返回false。這可以用于啟用或禁用控件,例如按鈕。CanExecuteChanged事件:當(dāng)CanExecute的返回值可能發(fā)生更改時(shí),應(yīng)引發(fā)此事件。
ICommand的結(jié)構(gòu)圖如下所示:

代碼如下所示:
public interface ICommand
{
event EventHandler? CanExecuteChanged;
bool CanExecute(object? parameter);
void Execute(object? parameter);
}
現(xiàn)在再來看看RelayCommand。
RelayCommand
RelayCommand是一種常用于WPF和MVVM模式的設(shè)計(jì)模式,它是一種特殊的命令類型。在MVVM模式中,RelayCommand允許將命令的處理邏輯從視圖模型中分離出來,使得視圖模型不需要知道命令的具體執(zhí)行邏輯,從而實(shí)現(xiàn)了視圖模型和命令處理邏輯的解耦。
RelayCommand通常包含兩個主要部分:CanExecute和Execute。CanExecute是一個返回布爾值的函數(shù),用于確定命令是否可以執(zhí)行。Execute是一個執(zhí)行命令的函數(shù),當(dāng)CanExecute返回true時(shí),Execute將被調(diào)用。
這種設(shè)計(jì)模式使得你可以在不改變視圖模型的情況下,更改命令的處理邏輯,提高了代碼的可維護(hù)性和可重用性。
簡單理解就是RelayCommand是ICommand接口的一個常見實(shí)現(xiàn),它允許你將Execute和CanExecute的邏輯定義為委托,從而實(shí)現(xiàn)對命令的靈活處理。
在RelayCommand中我們定義了兩個委托:
private Action<object> _Excute { get; set; }
private Predicate<object> _CanExcute { get;set; }
Action<object>是一個委托,它封裝了一個接受單個參數(shù)并且沒有返回值的方法。這個參數(shù)的類型是object。
對應(yīng)于這一部分:

Predicate<object>是一個委托,它封裝了一個接受單個參數(shù)并返回一個bool值的方法。這個參數(shù)的類型是object。
對應(yīng)于這一部分:

RelayCommand的構(gòu)造函數(shù)為:
public RelayCommand(Action<object> ExcuteMethod, Predicate<object> CanExcuteMethod)
{
_Excute = ExcuteMethod;
_CanExcute = CanExcuteMethod;
}
現(xiàn)在去看看View—ViewModel。
View—ViewModel
ViewModel是一個抽象,它代表了View的狀態(tài)和行為。ViewModel包含了View所需的數(shù)據(jù),并提供了命令以響應(yīng)View上的用戶操作。ViewModel不知道View的具體實(shí)現(xiàn),它只知道如何提供View所需的狀態(tài)和行為。
ViewModel的主要職責(zé)包括:
- 數(shù)據(jù)綁定:ViewModel提供了View所需的數(shù)據(jù)。這些數(shù)據(jù)通常是以屬性的形式提供的,當(dāng)這些屬性的值改變時(shí),ViewModel會通過實(shí)現(xiàn)
INotifyPropertyChanged接口來通知View。 - 命令綁定:ViewModel提供了命令以響應(yīng)View上的用戶操作。這些命令通常是以
ICommand接口的實(shí)現(xiàn)的形式提供的。 - 視圖邏輯:ViewModel包含了View的邏輯,例如,決定何時(shí)顯示或隱藏某個元素,何時(shí)啟用或禁用某個按鈕等。
新建一個ViewModel文件夾,在該文件夾中新建一個MainViewModel類:

目前寫的MainViewModel如下:
public class MainViewModel
{
public ObservableCollection<User> Users { get; set; }
public ICommand AddUserCommand { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public MainViewModel()
{
Users = UserManager.GetUsers();
AddUserCommand = new RelayCommand(AddUser, CanAddUser);
}
private bool CanAddUser(object obj)
{
return true;
}
private void AddUser(object obj)
{
User user = new User();
user.Name = Name;
user.Email = Email;
UserManager.AddUser(user);
}
}
現(xiàn)在我們結(jié)合這張圖,理解View與ViewModel之間的關(guān)系:

一個一個來理解,首先最重要的就是數(shù)據(jù)綁定。
現(xiàn)在View的xaml如下:
<Window x:Class="WPF_MVVM_Pattern.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:WPF_MVVM_Pattern"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<ToolBar>
<Label Content="姓名:"></Label>
<TextBox Text="{Binding Name}" Width="50"></TextBox>
<Label Content="郵箱:"></Label>
<TextBox Text="{Binding Email}" Width="100"></TextBox>
<Button Content="添加"
Command="{Binding AddUserCommand }"></Button>
</ToolBar>
<StackPanel>
<DataGrid ItemsSource="{Binding Users}"></DataGrid>
</StackPanel>
</StackPanel>
</Window>
cs如下:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel mainViewModel = new MainViewModel();
this.DataContext = mainViewModel;
}
}
將MainWindow的DataContext賦值給了mainViewModel。
在
<TextBox Text="{Binding Name}" Width="50"></TextBox>
<TextBox Text="{Binding Email}" Width="100"></TextBox>
<DataGrid ItemsSource="{Binding Users}"></DataGrid>
中進(jìn)行了數(shù)據(jù)綁定,對應(yīng)于圖中的這一部分:

現(xiàn)在來看看命令綁定。
<Button Content="添加"
Command="{Binding AddUserCommand }"></Button>
進(jìn)行了命令綁定,對應(yīng)于圖中這一部分:

現(xiàn)在先來看看效果:

現(xiàn)在已經(jīng)實(shí)現(xiàn)了與前面基于事件驅(qū)動同樣的效果,但是上面那張圖中的Send Notifications還沒有體現(xiàn)。
Send Notifications表示ViewModel中的更改會通知View。
現(xiàn)在我們來以一個例子說明一下Send Notifications是如何實(shí)現(xiàn)的。
首先添加一個測試命令:
public ICommand TestCommand { get; set; }
在構(gòu)造函數(shù)中添加:
TestCommand = new RelayCommand(Test, CanTest);
實(shí)現(xiàn)Test與CanTest方法:
private bool CanTest(object obj)
{
return true;
}
private void Test(object obj)
{
Name = "小1";
Email = "111@qq.com";
}
View中修改如下:
<Button Content="測試"
Command="{Binding TestCommand }"></Button>
現(xiàn)在去嘗試,我們會發(fā)現(xiàn)沒有效果,原因是我們的ViewModel沒有實(shí)現(xiàn)INotifyPropertyChanged接口。
INotifyPropertyChanged接口介紹
在WPF(Windows Presentation Foundation)中,INotifyPropertyChanged接口用于實(shí)現(xiàn)數(shù)據(jù)綁定中的屬性更改通知。當(dāng)綁定到UI元素的數(shù)據(jù)源中的屬性值發(fā)生更改時(shí),INotifyPropertyChanged接口可以通知UI元素更新。
INotifyPropertyChanged接口只定義了一個事件:PropertyChanged。當(dāng)屬性值發(fā)生更改時(shí),應(yīng)觸發(fā)此事件。事件參數(shù)PropertyChangedEventArgs包含更改的屬性的名稱。
現(xiàn)在我們的MainViewModel實(shí)現(xiàn)一下INotifyPropertyChanged接口,如下所示:
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<User> Users { get; set; }
public ICommand AddUserCommand { get; set; }
public ICommand TestCommand { get; set; }
private string? _name;
public string? Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
private string? _email;
public string? Email
{
get { return _email; }
set
{
if (_email != value)
{
_email = value;
OnPropertyChanged(nameof(Email));
}
}
}
public MainViewModel()
{
Users = UserManager.GetUsers();
AddUserCommand = new RelayCommand(AddUser, CanAddUser);
TestCommand = new RelayCommand(Test, CanTest);
}
private bool CanTest(object obj)
{
return true;
}
private void Test(object obj)
{
Name = "小1";
Email = "111@qq.com";
}
private bool CanAddUser(object obj)
{
return true;
}
private void AddUser(object obj)
{
User user = new User();
user.Name = Name;
user.Email = Email;
UserManager.AddUser(user);
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
現(xiàn)在再嘗試一下,會發(fā)現(xiàn)ViewModel中的更改會成功通知View了,如下所示:

對應(yīng)于圖中的這一部分:

現(xiàn)在我們來看看ViewModel—Model。
ViewModel—Model
現(xiàn)在我們來看看ViewModel與Model之間的關(guān)系,可以根據(jù)下面這張圖進(jìn)行理解:

Model(模型):Model代表了業(yè)務(wù)邏輯和數(shù)據(jù)。它包含了應(yīng)用程序中的數(shù)據(jù)和對數(shù)據(jù)的操作,例如,從數(shù)據(jù)庫中獲取數(shù)據(jù),或者向數(shù)據(jù)庫中添加數(shù)據(jù)。Model是獨(dú)立于UI的,它不知道UI的存在。
ViewModel(視圖模型):ViewModel是Model和View之間的橋梁。它包含了View所需的數(shù)據(jù)(這些數(shù)據(jù)來自于Model),并提供了命令以響應(yīng)View上的用戶操作。ViewModel將Model的數(shù)據(jù)轉(zhuǎn)換為View可以顯示的數(shù)據(jù),同時(shí),它也將View上的用戶操作轉(zhuǎn)換為對Model的操作。
在我們這個例子中我們的數(shù)據(jù)來源于Model文件夾下的User類與UserManager類:

這里的Send Notifications又該如何理解呢?
我們也是以一個小例子進(jìn)行說明。
首先將ViewModel中的Test方法修改為:
private void Test(object obj)
{
Users[0].Name = "小1";
Users[0].Email = "111@qq.com";
}
會發(fā)現(xiàn)現(xiàn)在并不會發(fā)送通知,實(shí)現(xiàn)View上的修改,這是因?yàn)閁ser類并沒有實(shí)現(xiàn)INotifyPropertyChanged接口,現(xiàn)在修改User類實(shí)現(xiàn)INotifyPropertyChanged接口:
public class User : INotifyPropertyChanged
{
private string? _name;
public string? Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
private string? _email;
public string? Email
{
get { return _email; }
set
{
if (_email != value)
{
_email = value;
OnPropertyChanged(nameof(Email));
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
現(xiàn)在可以實(shí)現(xiàn)通知了,效果如下所示:

使用MVVM庫
我們會發(fā)現(xiàn)如果全部都手動實(shí)現(xiàn)MVVM模式的話,代碼有點(diǎn)多,有點(diǎn)麻煩。這時(shí)候就可以使用一些MVVM庫來簡化我們的操作。
這里以CommunityToolkit.Mvvm為例,進(jìn)行說明。
CommunityToolkit.Mvvm介紹
CommunityToolkit.Mvvm是Microsoft Community Toolkit的一部分,它是一個輕量級但功能強(qiáng)大的MVVM(Model-View-ViewModel)庫,旨在幫助開發(fā)者更容易地實(shí)現(xiàn)MVVM設(shè)計(jì)模式。
該庫提供了一些基礎(chǔ)類,如ObservableObject和ObservableRecipient,這些類實(shí)現(xiàn)了INotifyPropertyChanged接口,并提供了SetProperty方法,可以在屬性值改變時(shí)觸發(fā)PropertyChanged事件。這使得數(shù)據(jù)綁定變得更加簡單和高效。
此外,該庫還提供了ICommand接口的實(shí)現(xiàn),如RelayCommand和AsyncRelayCommand,這些類可以幫助你創(chuàng)建命令,命令是MVVM模式中的一個重要組成部分。
CommunityToolkit.Mvvm還提供了一些其他有用的特性,如消息傳遞、設(shè)計(jì)時(shí)數(shù)據(jù)支持等,這些特性可以幫助你更好地組織和管理你的代碼。
CommunityToolkit.Mvvm是一個強(qiáng)大的工具,它可以幫助你更容易地實(shí)現(xiàn)MVVM模式,從而提高你的代碼質(zhì)量和開發(fā)效率。

修改之后的ViewModel如下所示:
public partial class MainViewModel : ObservableObject
{
public ObservableCollection<User> Users { get; set; }
[ObservableProperty]
private string? name;
[ObservableProperty]
private string? email;
public MainViewModel()
{
Users = UserManager.GetUsers();
}
[RelayCommand]
private void Test(object obj)
{
Users[0].Name = "小1";
Users[0].Email = "111@qq.com";
}
[RelayCommand]
private void AddUser(object obj)
{
User user = new User();
user.Name = Name;
user.Email = Email;
UserManager.AddUser(user);
}
}
修改之后的User類如下所示:
public partial class User : ObservableObject
{
[ObservableProperty]
private string? _name;
[ObservableProperty]
private string? _email;
}
用到了CommunityToolkit.Mvvm庫中的三個東西,分別是ObservableObject、[ObservableProperty]與[RelayCommand]。
先來看一下ObservableObject。
ObservableObject是CommunityToolkit.Mvvm庫中的一個基礎(chǔ)類,它實(shí)現(xiàn)了INotifyPropertyChanged接口。這個接口是.NET數(shù)據(jù)綁定基礎(chǔ)架構(gòu)的一部分,當(dāng)對象的一個屬性改變時(shí),它會通知綁定到該屬性的任何元素。


具體見:ObservableObject - Community Toolkits for .NET | Microsoft Learn
在這里我們使用
[ObservableProperty] private string? name;
它將生成一個像這樣的可觀察屬性:
public string? Name
{
get => name;
set => SetProperty(ref name, value);
}
具體見:ObservableProperty attribute - Community Toolkits for .NET | Microsoft Learn
我們使用
[RelayCommand]
private void AddUser(object obj)
{
User user = new User();
user.Name = Name;
user.Email = Email;
UserManager.AddUser(user);
}
代碼生成器會生成一個命令如下所示:
private RelayCommand? addUserCommand; public IRelayCommand AddUserCommand => addUserCommand ??= new RelayCommand(AddUser);
具體見:RelayCommand attribute - Community Toolkits for .NET | Microsoft Learn
現(xiàn)在我們的ViewModel與Model就可以簡化了,現(xiàn)在再來看看效果:

總結(jié)
本文先總體介紹了一下MVVM模式,關(guān)于MVVM模式可以根據(jù)這張圖幫助理解:

由于很多同學(xué)可能與我一樣,是從winform到wpf的,因此在wpf中使用winform中的事件驅(qū)動編程范式完成了一個小例子,關(guān)于事件驅(qū)動編程,可以根據(jù)這張圖幫助理解:

由于這種模式耦合比較多,我們想要松耦合,因此開始學(xué)習(xí)MVVM模式。我們創(chuàng)建了實(shí)現(xiàn)ICommand接口的RelayCommand類,實(shí)現(xiàn)INotifyPropertyChanged接口的MainViewModel類與User類。使用數(shù)據(jù)綁定與命令綁定改寫xaml頁面。
最后由于手動實(shí)現(xiàn)MVVM模式,需要寫很多代碼,看過去比較復(fù)雜與麻煩,我們可以使用MVVM庫來簡化MVVM模式的實(shí)現(xiàn)。
以上就是WPF中MVVM模式的理解與實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于WPF MVVM模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
unity shader實(shí)現(xiàn)較完整光照效果
這篇文章主要為大家詳細(xì)介紹了unity shader實(shí)現(xiàn)較完整光照效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
C#實(shí)現(xiàn)自定義動畫鼠標(biāo)的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)自定義動畫鼠標(biāo)效果,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12
c++ STL之list對結(jié)構(gòu)體的增加,刪除,排序等操作詳解
這篇文章主要介紹了c++ STL之list對結(jié)構(gòu)體的增加,刪除,排序等操作詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
C#實(shí)現(xiàn)將商品金額小寫轉(zhuǎn)換成大寫的方法
這篇文章主要介紹了C#實(shí)現(xiàn)將商品金額小寫轉(zhuǎn)換成大寫的方法,涉及C#數(shù)組與字符串的相關(guān)操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08

