ViewModel でコマンドを使う

完了

ビューモデルから UI にデータを取得する方法と、両方向のバインドを使用してビューモデルにデータを戻す方法を見てきました。

そのような両方向のバインドの使用は、"データ" が変更されたときすぐに UI からの変更に対応するために推奨される方法です。 "イベント" として処理する多くのものが、両方向のバインドと MVVM を使用して処理できます。 その他の例としては、イベントを使用しなくても、ブール値または整数値としてビューモデルに反映できる、Switch.IsToggledSlider.Value のようなものがあります。

ただし、ButtonMenuItem のアクティブ化のように、データの変更に直接結び付けられないものもあります。 これらの相互作用では、やはり "イベントのような" 処理が必要です。 これらの UI コンポーネントは通常、データを使用してなんらかのロジックを呼び出すので、ビューモデルでそのロジックが必要になります。 しかし、可能であれば、コードビハインドで ClickedSelected として処理したくはありません。 可能な限りビューモデルに留まることが望ましく、そうすることでテスト可能になります。

コマンド パターンを使う

この種の相互作用を持つ .NET MAUI コントロールの多くは、ICommand インターフェイスを公開するプロパティへのバインドをサポートしています。 このプロパティは、ほとんどの場合、Command という名前です。 ボタンが 1 つの例です。

<Button Text="Give Bonus" Command="{Binding GiveBonus}" />

コントロールは、コマンドを呼び出すタイミングを認識します。 たとえば、ボタンは、押されたときにコマンドを呼び出します。 この例のコマンドは、ビューモデルの GiveBonus プロパティにバインドされています。 プロパティ型は、ICommand インターフェイスを実装する必要があります。 コードは次のようになります。

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonus {get; private set;}
    ...
}

ICommand インターフェイスには、ボタンがクリックされると呼び出される Execute メソッドが含まれます。 このように、Button.Click のイベント処理コードは ICommand.Execute に直接置き換えられます。

完全な ICommand インターフェイスには、さらに CanExecute および CanExecuteChanged という 2 つのメソッドがあり、これらを使って、コントロールを有効または無効のどちらとして表示する必要があるかを決定します。

たとえば、CanExecute で false が返される場合、ボタンは選択不可になります。

C# コードで ICommand インターフェイスがどのようになるかを次に示します。

public interface ICommand
{
    bool CanExecute(object parameter);
    void Execute(object parameter);
    event EventHandler CanExecuteChanged;
}

Command クラスを使用する

このコマンド パターンを使うと、UI の動作と UI の実装を明確に分離しておくことができます。 ただし、各イベント ハンドラーを実装するために別々のクラスを作成する必要がある場合は、コードが複雑になる可能性があります。

インターフェイスを実装する複数のカスタム クラスを作成するのではなく、Command または Command<T> を使用する方が一般的です。 これらのクラスは ICommand を実装しますが、設定できるビューモデルのプロパティとして動作を公開します。 これにより、前に説明した GiveBonus プロパティをビューモデル クラス内で完全に実装することができます。

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonus {get; private set;}
    public EmployeeViewModel(Employee model)
    {
        GiveBonus = new Command(GiveBonusExecute, GiveBonusCanExecute)
    }

    void GiveBonusExecute()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
    }
}

このコードでは、Execute の動作は GiveBonusExecute メソッドによって提供されます。 そして、CanExecuteGiveBonusCanExecute によって提供されます。 これらのメソッドのデリゲートは Command コンストラクターに渡されます。 この例では、CanExecuteChanged の実装はありません。

コマンドとパラメーター

ICommand インターフェイスは、CanExecute メソッドと Execute メソッドの object パラメーターを受け取ります。 .NET MAUI は、Command クラスを介して型チェックを行うことなく、このインターフェイスを実装します。 コマンドにアタッチするデリゲートは、正しいパラメーターが渡されることを確認するために、独自の型チェックを実行する必要があります。 .NET MAUI には、必要なパラメーターの型を設定する Command<T> の実装も用意されています。 単一の型のパラメーターを受け入れるコマンドを作成する場合は、Command<T> を使用します。

コマンド パターンを実装する .NET MAUI コントロールは、CommandParameter プロパティを提供します。 このプロパティを設定すると、Execute を使用して呼び出されたときに、または CanExecute メソッドの状態をチェックするときに、パラメーターをコマンドに渡すことができます。

この例では、文字列値 25 がコマンドに送信されます。

<Button Text="Give Bonus" Command="{Binding GiveBonus}" CommandParameter="25" />

このコマンドでは、その文字列パラメーターを解釈して変換する必要があります。 厳密に型指定されたパラメーターを指定する方法は多数あります。

  • 属性構文を使用して CommandParameter を定義する代わりに、XAML 要素を使用します。

    <Button Text="Give Bonus" Command="{Binding GiveBonus}">
        <Button.CommandParameter>
            <x:Int32>25</x:Int32>
        </Button.CommandParameter>
    </Button>
    
  • CommandParameter を適切な型のインスタンスにバインドします。

  • CommandParameter が正しくない型にバインドされている場合は、コンバーターを適用して値を正しい型に変換します。

自分の知識をチェックする

1.

ICommand インターフェイスの目的は何ですか?

2.

Command クラスまたは Command<T> クラスでは、ICommand インターフェイスの使用をどのように簡略化できますか?

3.

.NET MAUI コントロールの CommandParameter プロパティの役割は何ですか?