Microsoft.Toolkit.Mvvm
(2023年時点では、CommunityToolkit.Mvvm
に名称が変更されています)
WindowsCommunityToolkit の中にあるMVVM用ライブラリの紹介Youtubeに以下のようなベンチマークが出ており、
そのパフォーマンスと省メモリーに興味がわいたので試してみました。
触って2日目ですので軽いご紹介です。
2022年1月13日更新:
GitHub のソースコード移動のためURL更新
2022年4月25日
新バージョン ver8の新機能を別投稿しています。
特徴
MVVM Lightにインスパイヤされた、よりモダンで軽量なライブラリ。同作者も携わっているとのこと。
- .NET Standard2.0対応
- .NET , .net Framework , mono , Xamarin , Uno などで動作。フレームワークに依存しない
- Mvvm部分だけ単独で利用可能(Toolkit全体は不要)。
- MVVMLightやPrismに比べるとかなり省メモリらしい。
-
INotifyPropertyChanged
,ICommand
,Messenger
,DI(IoC)
の4機能を提供。それぞれ独立しているので使いたいものだけ使える。 - MITライセンス
インストール
nugetで Microsoft.Toolkit.Mvvm
CommunityToolkit.Mvvm
をインストールすればOK。
.NET5以降なら依存関係なし、40kBぐらいのDLLが1つのみでした。
使い方
4つの機能がありますが独立しているのでObservableObjectだけ利用するなど好きなものを使えます。詳しくはサンプルドキュメントやソースコードで確認してください。
提供機能 | 実装(例) | Namespace |
---|---|---|
INotifyPropertyChanged | ObservableObject | CommunityToolkit.Mvvm.ComponentModel |
ICommand | RelayCommand | CommunityToolkit.Mvvm.Input |
Messenger | WeakReferenceMessenger | CommunityToolkit.Mvvm.Messeging |
DI, IoC | IoC | CommunityToolkit.Mvvm.DependencyInjection |
(修正:名前空間はMicrosoft.Toolkit.Mvvm
からCommunityToolkit.Mvvm
に変更になりました)
ObservableObject
INotifyPropertyChanged
の実装です。他のMVVMフレームワークでいう BindableBase
などと同様で継承して使います。
//using Microsoft.Toolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
public class VM1 : ObservableObject
{
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
}
また、INofityPropertyChanged
を実装していないModelクラスをラップしてPropertyChanged
に反応させることもできます。
//ViewModelで使いたいModelクラス
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
//ViewModelでラップしてPropertyChangedに反応させる
public class VM1 : ObservableObject
{
private Person _person;
public int Age
{
get => _person.Age;
set => SetProperty(_person.Age, value, _person, (p, newval) => p.Age = newval);
}
public string Name
{
get => _person.Name;
set => SetProperty(_person.Name, value, _person, (p, newval) => p.Name = newval);
//SetProperty(古値, 新値, 対象オブジェクト, 値更新(古値!=新値)時に実行するcallback);
}
}
ObservableObject
の派生として、IMessenger 機能を有したObservableRecipient
(後述)や
INotifyDataErrorInfo も実装したObservableValidator
もあるのでそれらの機能が必要になったときにVMの継承を変えればMessengerやValidationが使えるようになります。
RelayCommand
ICommand
の実装です。使い勝手も他MVVMライブラリでいうDelegateCommand
等とほぼ同様。
//using Microsoft.Toolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Input;
public class VM1 : ObservableObject
{
public ICommand ClickCommand { get; }
public VM1()
{
ClickCommand = new RelayCommand(Click);
}
private void Click() { Name = ""; }
}
CanExecute
や CommandParameter
にも当然対応。
RelayCommand(Action execute);
RelayCommand(Action execute, Func<bool> canExecute);
RelayCommand(Action<T?> execute);
RelayCommand(Action<T?> execute, Predicate<T?> canExecute);
非同期版もあり Func<Task>
を利用できます。非同期版は CancellationToken
を受けることも可能です。これでだいぶ使いやすくなりました。
AsyncRelayCommand(Func<Task>)
AsyncRelayCommand(Func<Task>, Func<Boolean>)
AsyncRelayCommand(Func<CancellationToken,Task>)
AsyncRelayCommand(Func<CancellationToken,Task>, Func<Boolean>)
AsyncRelayCommand<T>(Func<T,Task>)
AsyncRelayCommand<T>(Func<T,Task>, Predicate<T>)
AsyncRelayCommand<T>(Func<T,CancellationToken,Task>)
AsyncRelayCommand<T>(Func<T,CancellationToken,Task>, Predicate<T>)
Messenger
おそらくこのライブラリの一番の特徴はこの Messenger 機能で、MvvmLight と比べるとメモリ利用は100分の1以下、実行速度も5~10倍以上というベンチマーク結果がでています。(紹介Youtubeから)
弱参照でメモリーを自動管理してくれるWeakReferenceMessengerと強参照版のStrongReferenceMessengerがありますが、今回は弱参照の方を使ってViewとViewModel間のメッセージングをしてみます。
まずは送受信用メッセージとして小さなクラスを作ります。
//using Microsoft.Toolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging;
public class MyMessage : ValueChangedMessage<string>
{
public MyMessage(string value) : base(value)
{
}
}
参照型ならOKなので、stringをそのまま送信しても動きますが今回はライブラリが用意している値変更用クラス ValueChangedMessage<T>
を継承しました。(ソース 見るとわかりますが、Value プロパティがあるだけの実質1行のクラスです)
このメッセージをViewとViewModel間で送受信します。送信側は単純です。
//using Microsoft.Toolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging;
//送信したいところで
WeakReferenceMessenger.Default.Send(new MyMessage("Hello"));
受信側は、まずコンストラクタなどで受信コールバックを登録します。これで受信できるようになります。
//using Microsoft.Toolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging;
//受信関数recv()を登録(コンストラクタ等)
WeakReferenceMessenger.Default.Register<MyMessage>(this, recv);
private void recv(object recipient, MyMessage message)
{
//受信処理
Console.WriteLine(message.Value); //"Hello"と出力
}
ViewModelで受信する場合は継承元クラスをObservableRecipient
にすることで登録処理をすこし省略できます。ObservableObject
を継承しているので PropertyChanged イベントも対応しています。
//using Microsoft.Toolkit.Mvvm.ComponentModel;
//using Microsoft.Toolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
public class VM2 : ObservableRecipient, IRecipient<MyMessage>
{
public VM2()
{
//メッセージ受信を有効
IsActive = true;
}
//受信部:IRecipientインターフェースの実装
public void Receive(MyMessage message)
{
Console.WriteLine(message.Value); //"Hello"と出力
}
}
いらなくなったらメッセージ購読解除。
WeakReferenceMessenger.Default.Unregister<MyMessage>(this);
強参照版も使い方はほぼ一緒です。
他に用意されているメッセージタイプ等についてはサンプルのドキュメントやソースコードをご確認ください。
IoC/DI
Inversion of Control(制御の反転)を実装したDI向けクラス。
ソースコードを見ればわかりますが必要最小限のヘルパーです。そのためIServiceProviderを実装する別のDIライブラリが必要になります。ここでは Microsoft.Extensions.DependencyInjection
を使って自作のILoggerでも登録してみます。
登録するサービスはサンプルとして自作のMyLoggerにしてみます。
public interface ILogger
{
void Log(string text);
}
public class MyLogger : ILogger
{
public void Log(string text) => Console.WriteLine(text);
}
これをサービスとして登録します。今回はWPFにしたのでapp.xaml.csで登録します。IoC.Default
というstaticプロパティが用意されているのでそれを使いシングルトンとして登録します。ConfigureServices()に指定する引数 IServceProvider はMicrosoft.Extensions.DependencyInjection
を利用してます。
//using Microsoft.Toolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
public partial class App : Application
{
public App()
{
this.InitializeComponent();
Ioc.Default.ConfigureServices(
new ServiceCollection()
.AddSingleton<ILogger,MyLogger>()
.BuildServiceProvider());
}
}
登録したMyLoggerをViewModelで使ってみます。
//using Microsoft.Toolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.DependencyInjection;
public void Receive(MyMessage message)
{
Ioc.Default.GetService<ILogger>().Log(message.Value);
}
参考
ソースコード
https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Mvvm
2022年1月現在、以下に移動しています。
https://github.com/CommunityToolkit/dotnet
サンプルとドキュメント
https://github.com/windows-toolkit/MVVM-Samples/tree/master/docs/mvvm
公式ドキュメント