ViewModel でコマンドを使う
ビューモデルから UI にデータを取得する方法と、両方向のバインドを使用してビューモデルにデータを戻す方法を見てきました。
そのような両方向のバインドの使用は、"データ" が変更されたときすぐに UI からの変更に対応するために推奨される方法です。 "イベント" として処理する多くのものが、両方向のバインドと MVVM を使用して処理できます。 その他の例としては、イベントを使用しなくても、ブール値または整数値としてビューモデルに反映できる、Switch.IsToggled
や Slider.Value
のようなものがあります。
ただし、Button
や MenuItem
のアクティブ化のように、データの変更に直接結び付けられないものもあります。 これらの相互作用では、やはり "イベントのような" 処理が必要です。 これらの UI コンポーネントは通常、データを使用してなんらかのロジックを呼び出すので、ビューモデルでそのロジックが必要になります。 しかし、可能であれば、コードビハインドで Clicked
や Selected
として処理したくはありません。 可能な限りビューモデルに留まることが望ましく、そうすることでテスト可能になります。
コマンド パターンを使う
この種の相互作用を持つ .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
メソッドによって提供されます。 そして、CanExecute
は GiveBonusCanExecute
によって提供されます。 これらのメソッドのデリゲートは 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
が正しくない型にバインドされている場合は、コンバーターを適用して値を正しい型に変換します。