1. 什么是 MVVM Toolkit
模型-視圖-視圖模型 (MVVM) 是用于解耦 UI 代碼和非 UI 代碼的 UI 體系結構設計模式, 借助 MVVM,可以在 XAML 中以宣告方式定義 UI,并使用資料系結標記將 UI 鏈接到包含資料和命令的其他層,
微軟雖然提出了 MVVM,但又沒有提供一個官方的 MVVM 庫(多年前有過 Prism,但已經離家出走了),每次有人提起 MVVM 庫,有些人會推薦 Prism(例如我),有些人會推薦 MVVMLight,可是現在 Prism 已經決定不再支持 UWP , 而 MVVMLight 又不再更新,在這左右為難的時候 Windows Community Toolkit 挺身而出發布了 MVVM Toolkit, MVVM Toolkit 延續了 MVVMLight 的風格,是一個輕量級的組件,而且它基于 .NET Standard 2.0,可用于UWP, WinForms, WPF, Xamarin, Uno 等多個平臺,相比它的前身 MVVMLight,它有以下特點:
- 更高:版本號更高,一出手就是 7.0,
- 更快:速度更快,MVVM Toolkit 從一開始就以高性能為實作目標,
- 更強:后臺更強,MVVM Toolkit 的全程是 'Microsoft.Toolkit.Mvvm',根正苗紅,
目前,MVVM Toolkit 已經更新到 '7.0.2',它的詳細資料可以參考下面鏈接:
Nuget:https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm
檔案:https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction
原始碼:https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Mvvm
雖然是 Windows Community Toolkit 專案的一部分,但它有獨立的 Sample 和檔案,可以在這里找到:
https://github.com/CommunityToolkit/MVVM-Samples
這篇文章將簡單介紹 MVVM Toolkit 的幾個基本組件,
2. 各個組件
2.1 ObservableObject
ObservableObject 實作了 INotifyPropertyChanged 和INotifyPropertyChanging,并觸發 PropertyChanged 和 PropertyChanging 事件,
public class User : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}
在這段示例代碼中,如果 name 和 value 的值不同,首先觸發 PropertyChanging 事件,然后觸發 PropertyChanged,
2.2 RelayCommand
RelayCommand 和 RelayCommand<T> 實作了 ICommand 介面,INotifyPropertyChanged 和 ICommand 是 MVVM 模式的基礎,下面的代碼使用 ObservableObject 和 RelayCommand 展示一個基本的 ViewModel:
public class MyViewModel : ObservableObject
{
public MyViewModel()
{
IncrementCounterCommand = new RelayCommand(IncrementCounter);
}
private int counter;
public int Counter
{
get => counter;
private set => SetProperty(ref counter, value);
}
public ICommand IncrementCounterCommand { get; }
private void IncrementCounter() => Counter++;
}
<Page
x:
xmlns:viewModels="using:MyApp.ViewModels">
<Page.DataContext>
<viewModels:MyViewModel x:Name="ViewModel"/>
</Page.DataContext>
<StackPanel Spacing="8">
<TextBlock Text="{x:Bind ViewModel.Counter, Mode=OneWay}"/>
<Button
Content="Click me!"
Command="{x:Bind ViewModel.IncrementCounterCommand}"/>
</StackPanel>
</Page>
在這段示例里 IncrementCounterCommand 包裝了 IncrementCounter 函式提供給 Button 系結,IncrementCounter 函式更改 Counter 的值并通過 PropertyChanged 事件通知系結的 TextBlock,
2.3 AsyncRelayCommand
AsyncRelayCommand 和 AsyncRelayCommand<T> 也實作了 ICommand,不過它們支持異步操作,提供的 ExecutionTask 和 IsRunning 兩個屬性對監視任務運行狀態十分有用,
例如這個 ViewModel:
public MyViewModel()
{
DownloadTextCommand = new AsyncRelayCommand(DownloadTextAsync);
}
public IAsyncRelayCommand DownloadTextCommand { get; }
private async Task<string> DownloadTextAsync()
{
await Task.Delay(3000); // Simulate a web request
return "Hello world!";
}
使用相關的 UI 代碼:
<Page.Resources>
<converters:TaskResultConverter x:Key="TaskResultConverter"/>
</Page.Resources>
<StackPanel Spacing="8">
<TextBlock>
<Run Text="Task status:"/>
<Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask.Status, Mode=OneWay}"/>
<LineBreak/>
<Run Text="Result:"/>
<Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask, Converter={StaticResource TaskResultConverter}, Mode=OneWay}"/>
</TextBlock>
<Button
Content="Click me!"
Command="{x:Bind ViewModel.DownloadTextCommand}"/>
<muxc:ProgressRing
HorizontalAlignment="Left"
IsActive="{x:Bind ViewModel.DownloadTextCommand.IsRunning, Mode=OneWay}"/>
</StackPanel>
點擊 Button 后 DownloadTextAsync 開始運行,在 UI 上 TextBlock 和 ProgressRing 系結到 ExecutionTask 和 IsRunning 并顯示任務運行狀態,最后通過 TaskResultConverter 顯示任務結果,

2.4 Messenger
對于主要目的是松耦合的 MVVM 框架,提供一個用于訊息交換的系統十分有必要,MVVM Toolkit 中用于訊息交換的核心是 WeakReferenceMessenger 類,
// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
public LoggedInUserChangedMessage(User user) : base(user)
{
}
}
// Register a message in some module
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
{
// Handle the message here, with r being the recipient and m being the
// input messenger. Using the recipient passed as input makes it so that
// the lambda expression doesn't capture "this", improving performance.
});
// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));
正如這段代碼所示,WeakReferenceMessenger 主要通過 Register 和 Send 進行資訊交換,它的使用方式類似于 MVVMLight 的 messenger 類,MVVM Toolkit 另外還提供了一個 StrongReferenceMessenger 類,更多使用方法可以參考這篇 檔案,Messenger 功能強大且簡單易用,但也由于誤用會帶來風險而引發了一些爭議,有必要更詳細地理解它的原理和用法以避免它帶來的其它風險,這篇文章只是簡單地介紹一下它的用法,
2.5 ObservableRecipient
ObservableRecipient 繼承了 ObservableObject 并支持從 Messenger 接收資訊,可通過 IsActive 屬性激活或停用,它可以用作 ViewModel 的基類,事實上它的作用基本上相遇于 MVVMLight 中的 ViewModelBase :
public class MyViewModel : ObservableRecipient, IRecipient<LoggedInUserRequestMessage>
{
public void Receive(LoggedInUserRequestMessage message)
{
// Handle the message here
}
}
3. The 性能

MVVM Toolkit 在開發程序中為了追求卓越的性能做了很多努力,例如提供一個 StrongReferenceMessenger 類,性能如上圖所示地有了大幅提升,又例如下面這篇文章所介紹的:
MVVM Toolkit Preview 3 & The Journey of an API
有興趣的話可以通過原始碼詳細了解一下,
4. 結語
這篇文章簡單介紹了 MVVM Toolkit 中的主要功能,更多內容可參考 原始碼、單元測驗 或 windows-toolkit/MVVM-Samples 中提供的示例應用:

5. 參考
Sample repo for MVVM package
Microsoft.Toolkit.Mvvm at master
[Feature] Basic MVVM primitives (.NET Standard)
NuGet Gallery _ Microsoft.Toolkit.Mvvm
MVVM Light Toolkit
資料系結和 MVVM
[Feature] Microsoft.Toolkit.Mvvm package (Preview 5)
MVVM Toolkit Preview 3 & The Journey of an API
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/294831.html
標籤:WPF
