次の方法で共有


MVVM Toolkit の機能

ヒント

この内容は電子ブック『.NET MAUI を使用したエンタープライズ アプリケーション パターン』からの抜粋です。これは .NET Docs で閲覧することも、無料の PDF をダウンロードしてオフラインで読むこともできます。

電子ブック『.NET MAUI を使用したエンタープライズ アプリケーション パターン』の表紙のサムネイル。

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 オブジェクトまたは変更通知を生成する必要がある任意のオブジェクトのベースとして使われることです。 そこでは、INotifyPropertyChangedINotifyPropertyChanging およびプロパティを設定して変更を発生させるためのヘルパー メソッドが実装されています。 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 メソッドを使って、タスクが完了するまでプロパティの変更の発行を遅らせることができます。 メソッド OnPropertyChangedOnPropertyChanging は、オブジェクトで必要に応じてプロパティの変更を生成するために使うこともできます。

ObservableObject について詳しくは、MVVM Toolkit デベロッパー センターの「ObservableObject」をご覧ください。

RelayCommand および AsyncRelayCommand

.NET MAUI コントロール (たとえば、ボタンのタップや、コレクションからの項目の選択) と ViewModel の間の相互作用は、ICommand インターフェイスを使って行われます。 .NET MAUI には、Command オブジェクトを含む ICommandの既定の実装が付属しています。 .NET MAUI の Command は非常に基本的であり、非同期処理やコマンド実行状態のサポートなど、高度な機能はサポートされていません。

MVVM ツールキットには、RelayCommandAsyncRelayCommand の 2 つのコマンドが付属しています。 RelayCommand の対象となる状況は、実行する同期コードがあり、実装が .NET MAUI の Command オブジェクトとよく似ている場合です。

注意

.NET MAUI の CommandRelayCommand は似ていますが、RelayCommand を使うと、ViewModel を .NET MAUI の直接参照から切り離すことができます。 これは、ViewModel の移植性が高くなり、プロジェクト間で再利用しやすくなることを意味します。

AsyncRelayCommand には、非同期ワークフローを操作するときの多くの追加機能が用意されています。 通常、リポジトリ、API、データベース、および async/await を利用する他のシステムと通信するため、これは ViewModel では非常に一般的です。 AsyncRelayCommand コンストラクターは、Func<Task> として定義されてい実行タスク、またはコンストラクターの一部として Task を返すデリゲートを受け取ります。 実行タスクが実行している間、AsyncRelayCommand はタスクの状態を監視し、IsRunning プロパティを使って更新を提供します。 IsRunning プロパティは UI にバインドできます。これは、ActivityIndicator を使って読み込み中を示したり、コントロールを無効または有効にしたりするなど、コントロールの状態を管理するのに便利です。 実行タスクの実行中に、Cancel メソッドを呼び出して実行タスクの取り消しを試みることができます (サポートされている場合)。

既定の AsyncRelayCommand では、同時実行は許可されません。 これは、ユーザーがうっかりコントロールを複数回タップして、実行時間の長い操作やコストのかかる操作を実行する可能性がある場合に、非常に役立ちます。 タスクの実行中に、AsyncRelayCommandCanExecuteChanged イベントを自動的に呼び出します。 .NET MAUI では、CommandCommandParameter プロパティをサポートするコントロール (Button など) は、このイベントをリッスンし、実行中にそれを自動的に有効または無効にします。 この機能は、カスタム canExecute パラメーターを使うことで、またはコンストラクターで AsyncRelayCommandOptions.AllowConcurrentExecutions フラグを設定することで、オーバーライドできます。

コマンドの実装について詳しくは、MVVM の章の「コマンドの実装」セクションをご覧ください。 RelayCommandAsyncRelayCommand について詳しくは、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 は、ObservableObjectAsync/RelayCommand などの標準的な MVVM コンポーネントの優れた実装を提供します。 ソース ジェネレーターは、ユーザー インターフェイスの操作に必要なすべての定型コードを生成することで、ViewModel のプロパティとコマンドを簡略化するのに役立ちます。 MVVM Toolkit は、この章で説明した機能以外にもさらに多くの機能を提供します。 MVVM Toolkit について詳しくは、MVVM Toolkit デベロッパー センター「「MVVM Toolkit の概要」をご覧ください。