MVVM Toolkit の機能
ヒント
この内容は電子ブック『.NET MAUI を使用したエンタープライズ アプリケーション パターン』からの抜粋です。これは .NET Docs で閲覧することも、無料の PDF をダウンロードしてオフラインで読むこともできます。
MVVM Toolkit
Model-View-ViewModel (MVVM) パターンは、アプリケーションを作成するための優れた構造基盤です。 このパターンでは、フロントエンド ユーザー インターフェイスとバッキング コンポーネントへの通信を提供する ViewModel が、アプリケーションのバックボーンになります。 ユーザー インターフェイスとの統合を提供するには、ViewModel のプロパティとコマンドを利用します。 「基になるビュー モデルまたはモデルの変更に応じてビューを更新する」で詳しく説明されているように、ViewModel の INotifyPropertyChanged
インターフェイスを使うと、プロパティの値が変更されたときに通知できます。 これらの機能をすべて実装すると、ViewModel が非常に冗長になる可能性があります。 たとえば、次に示すコードは、変更を発生させるプロパティを含む単純な ViewModel です。
public class SampleViewModel : INotifyPropertyChanged
{
private string _name;
private int _value;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get => _name;
set => SetPropertyValue(ref _name, value);
}
public int Value
{
get => _value;
set => SetPropertyValue(ref _value, value);
}
protected void SetPropertyValue<T>(ref T storageField, T newValue, [CallerMemberName] string propertyName = "")
{
if (Equals(storageField, newValue))
return;
storageField = newValue;
RaisePropertyChanged(propertyName);
}
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
一部の最適化は時間をかけて行われる可能性がありますが、それでも ViewModel を定義するためのコード セットは非常に冗長になります。 このコードは保守が困難であり、エラーが発生しやすくなります。
CommunityToolkit.Mvvm NuGet パッケージ (MVVM Toolkit とも呼ばれます) を使うと、これらの一般的な MVVM パターンに対処して簡単にできます。 MVVM Toolkit と、.NET 言語の新しい機能では、簡単なロジック、プロジェクトへの簡単な導入、ランタイムの独立性が考慮されています。 次に示す例は、MVVM Toolkit に付属するコンポーネントを同じ ViewModel に使った場合です。
public partial class SampleViewModel : ObservableObject
{
[ObservableProperty]
private string _name;
[ObservableProperty]
private int _value;
}
注意
MVVM Toolkit は CommunityToolkit.Mvvm
パッケージで提供されます。 プロジェクトにパッケージを追加する方法については、Microsoft デベロッパー センターの「MVVM Toolkit の概要」を参照してください。
元の例と比べると、全体的な複雑さを大幅に減らし、ViewModel を保守しやすくすることができました。 MVVM Toolkit に付属する、上で見た ObservableObject
のような多くの事前構築済みの共通コンポーネントと機能により、アプリケーション全体のコードが簡略化および標準化されます。
ObservableObject
MVVM ツールキットに含まれる ObservableObject
の目的は、ViewModel
オブジェクトまたは変更通知を生成する必要がある任意のオブジェクトのベースとして使われることです。 そこでは、INotifyPropertyChanged
と INotifyPropertyChanging
およびプロパティを設定して変更を発生させるためのヘルパー メソッドが実装されています。 ObservableObject
を使用する標準的な ViewModel の例を次に示します。
public class SampleViewModel : ObservableObject
{
private string _name;
private int _value;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public int Value
{
get => _value;
set => SetProperty(ref _value, value);
}
}
ObservableObject
は、プロパティ セッターの SetProperty
メソッドを使って変更通知を生成するために必要なすべてのロジックを処理します。 Task<T>
を返すプロパティがある場合、SetPropertyAndNotifyOnCompletion
メソッドを使って、タスクが完了するまでプロパティの変更の発行を遅らせることができます。 メソッド OnPropertyChanged
と OnPropertyChanging
は、オブジェクトで必要に応じてプロパティの変更を生成するために使うこともできます。
ObservableObject
について詳しくは、MVVM Toolkit デベロッパー センターの「ObservableObject」をご覧ください。
RelayCommand
および AsyncRelayCommand
.NET MAUI コントロール (たとえば、ボタンのタップや、コレクションからの項目の選択) と ViewModel の間の相互作用は、ICommand
インターフェイスを使って行われます。 .NET MAUI には、Command
オブジェクトを含む ICommand
の既定の実装が付属しています。 .NET MAUI の Command
は非常に基本的であり、非同期処理やコマンド実行状態のサポートなど、高度な機能はサポートされていません。
MVVM ツールキットには、RelayCommand
と AsyncRelayCommand
の 2 つのコマンドが付属しています。 RelayCommand
の対象となる状況は、実行する同期コードがあり、実装が .NET MAUI の Command
オブジェクトとよく似ている場合です。
注意
.NET MAUI の Command
と RelayCommand
は似ていますが、RelayCommand
を使うと、ViewModel を .NET MAUI の直接参照から切り離すことができます。 これは、ViewModel の移植性が高くなり、プロジェクト間で再利用しやすくなることを意味します。
AsyncRelayCommand
には、非同期ワークフローを操作するときの多くの追加機能が用意されています。 通常、リポジトリ、API、データベース、および async/await
を利用する他のシステムと通信するため、これは ViewModel では非常に一般的です。 AsyncRelayCommand
コンストラクターは、Func<Task>
として定義されてい実行タスク、またはコンストラクターの一部として Task
を返すデリゲートを受け取ります。 実行タスクが実行している間、AsyncRelayCommand
はタスクの状態を監視し、IsRunning
プロパティを使って更新を提供します。 IsRunning
プロパティは UI にバインドできます。これは、ActivityIndicator
を使って読み込み中を示したり、コントロールを無効または有効にしたりするなど、コントロールの状態を管理するのに便利です。 実行タスクの実行中に、Cancel
メソッドを呼び出して実行タスクの取り消しを試みることができます (サポートされている場合)。
既定の AsyncRelayCommand
では、同時実行は許可されません。 これは、ユーザーがうっかりコントロールを複数回タップして、実行時間の長い操作やコストのかかる操作を実行する可能性がある場合に、非常に役立ちます。 タスクの実行中に、AsyncRelayCommand
は CanExecuteChanged
イベントを自動的に呼び出します。 .NET MAUI では、Command
と CommandParameter
プロパティをサポートするコントロール (Button
など) は、このイベントをリッスンし、実行中にそれを自動的に有効または無効にします。 この機能は、カスタム canExecute
パラメーターを使うことで、またはコンストラクターで AsyncRelayCommandOptions.AllowConcurrentExecutions
フラグを設定することで、オーバーライドできます。
コマンドの実装について詳しくは、MVVM の章の「コマンドの実装」セクションをご覧ください。 RelayCommand
と AsyncRelayCommand
について詳しくは、MVVM Toolkit デベロッパー センターのコマンド実行に関する記事をご覧ください。
ソース ジェネレーター
すぐに使用できる MVVM Toolkit コンポーネントを使うと、ViewModel を大幅に簡単にできます。 MVVM Toolkit のソース ジェネレーターを使うと、一般的なコードのユース ケースをさらに簡単にできます。 MVVM Toolkit のソース ジェネレーターは、コード内で特定の属性を見つけて、プロパティとコマンドのラッパーを生成できます。
重要
MVVM Toolkit のソース ジェネレーターは、既存のオブジェクトに追加されるコードを生成します。 このため、ソース ジェネレーターを利用するすべてのオブジェクトを、partial
とマークする必要があります。
MVVM Toolkit の ObservableProperty
属性は、ObservableObject
を継承するオブジェクトのフィールドに適用でき、変更を生成するプロパティでプライベート フィールドをラップします。 次に示すのは、_name
フィールドで ObservableObject
属性を使用するコードの例です。
public partial class SampleViewModel : ObservableObject
{
[ObservableProperty]
private string _name;
}
_name
フィールドに ObservableProperty
属性を適用すると、ソース ジェネレーターが実行されて、次のコードを含む別の部分クラスが生成されます。
partial class SampleViewModel
{
public string Name
{
get => _name;
set
{
if (!EqualityComparer<string>.Default.Equals(_name, value))
{
OnNameChanging(value);
OnPropertyChanging("Name");
_name = value;
OnNameChanged(value);
OnPropertyChanged("Name");
}
}
}
}
生成された SampleViewModel
では、プライベート _name
フィールドが使われており、変更通知を生成するために必要なすべてのロジックを実装する新しい Name
プロパティが生成されています。
MVVM Toolkit の RelayCommand
属性は、ObservableObject
内のメソッドに適用でき、対応する RelayCommand
または AsyncRelayCommand
を作成します。 次に示すコードは、RelayCommand
属性を使う例です。
public partial class SampleViewModel : ObservableObject
{
public INavigationService NavigationService { get; set; }
[ObservableProperty]
private string _name;
[ObservableProperty]
bool _isValid;
[RelayCommand]
private Task SettingsAsync()
{
return NavigationService.NavigateToAsync("Settings");
}
[RelayCommand]
private void Validate()
{
IsValid = !string.IsNullOrEmpty(Name);
}
}
Validate
メソッドに適用された RelayCommand
は、void
の戻り値を持つので RelayCommand
検証 ValidateCommand
を生成し、SettingsAsync
メソッドは SettingsCommand
という名前の AsyncRelayCommand
を生成します。 ソース ジェネレーターは、他の部分クラスに次のコードを生成します。
partial class SampleViewModel
{
private AsyncRelayCommand? settingsCommand;
SettingsCommand => settingsCommand ??= new AsyncRelayCommand(SettingsAsync);
}
partial class SampleViewModel
{
private RelayCommand? validateCommand;
public IRelayCommand ValidateCommand => validateCommand ??= new RelayCommand(Validate);
}
ViewModel のメソッドを ICommand
の実装でラップする複雑さはすべて、ソース ジェネレーターによって処理されています。
MVVM Toolkit のソース ジェネレーターについて詳しくは、MVVM Toolkit デベロッパー センターの「MVVM ソース ジェネレーター」をご覧ください。
まとめ
MVVM Toolkit は、ViewModel のコードを標準化して簡単にするための優れた方法です。 MVVM Toolkit は、ObservableObject
や Async/RelayCommand
などの標準的な MVVM コンポーネントの優れた実装を提供します。 ソース ジェネレーターは、ユーザー インターフェイスの操作に必要なすべての定型コードを生成することで、ViewModel のプロパティとコマンドを簡略化するのに役立ちます。 MVVM Toolkit は、この章で説明した機能以外にもさらに多くの機能を提供します。 MVVM Toolkit について詳しくは、MVVM Toolkit デベロッパー センター「「MVVM Toolkit の概要」をご覧ください。
.NET