在 ViewModel 中使用命令
您已看到如何將資料從 viewmodel 移至 UI,以及如何使用雙向繫結,將資料移回 viewmodel。
使用這樣的雙向繫結,是在「資料」發生變更時,回應 UI 變更的慣用方法。 許多可作為「事件」處理的項目,都可以使用雙向繫結與 Model-View-ViewModel (MVVM) 模式來處理。 其他範例,例如 Switch.IsToggled
和 Slider.Value
,可以在我們的 ViewModel 中反映為布林值或整數值,而不必使用事件。
但有些項目,例如 Button
或 MenuItem
啟用,就與資料變更沒有直接關係。 這些互動仍然需要「類似事件」處理。 由於這些 UI 元件通常會使用資料叫用某種邏輯,因此我們想要該邏輯在 ViewModel 上。 但是,我們不想要在程式碼後置中將其當成 Clicked
和 Selected
事件處理,如果可能的話。 我們想要盡可能在 ViewModel 中,如此一來,就可以對其進行測試。
使用命令模式
許多具有這種互動的 .NET MAUI 控制項都支援繫結至公開 ICommand
介面的屬性。 這個屬性最有可能命名為 Command
。 Button
控制項是一個範例:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" />
控件項知道何時要叫用命令。 例如,按鈕會在按下時叫用命令。 此範例中的命令會繫結至 ViewModel 的 GiveBonus
屬性。 屬性類型必須實作 ICommand
介面。 程式碼看起來應該類似下列範例:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
...
}
ICommand
介面具有 Execute
方法,按一下該按鈕即會呼叫此方法。 如此一來,ICommand.Execute
可直接取代 Button.Click
事件處理程式碼。
完整的 ICommand
介面還有兩個方法:CanExecute
和 CanExecuteChanged
,用來決定控制項應顯示已啟用還是已停用。
例如,如果 CanExecute
傳回 false,則按鈕可能顯示為灰色。
以下是 ICommand
介面在 C# 中的外觀:
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
使用命令類別
此命令模式可讓您清楚地分離 UI 行為與 UI 實作。 但是,如果您需要建立個別的類別來實作每個事件處理常式,它可能會讓程式碼變得很複雜。
通常使用 Command
或 Command<T>
,而不是建立數個實作介面的自訂類別。 這些類別會實作 ICommand
,但會將其行為公開為 ViewModel 中您可以設定的屬性。 使用此方式,我們可以實作 GiveBonus
屬性,稍早已在我們的 viewmodel 類別內完整描述此屬性:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
public EmployeeViewModel(Employee model)
{
GiveBonusCommand = new Command(GiveBonusExecute, GiveBonusCanExecute)
}
void GiveBonusExecute()
{
//logic for giving bonus
}
bool GiveBonusCanExecute()
{
//logic for deciding if "give bonus" button should be enabled.
}
}
在此程式碼中,方法 GiveBonusExecute
提供 Execute
行為。 而 GiveBonusCanExecute
提供 CanExecute
。 這些方法的委派將傳遞至 Command
建構函式。 在此範例中,不實作 CanExecuteChanged
。
使用 MVVM 工具組簡化
MVVM 工具組程式庫包含 ICommand
的實作,亦稱為 RelayCommand
和 AsyncRelayCommand
。 它也提供來源產生器,以進一步簡化此程式碼。 在下列範例中,會產生 GiveBonusCommand
,同時設定呼叫以執行的方法,以及呼叫以查看它是否可執行的方法。 [RelayCommand]
屬性會用於 GiveBonus
方法,並會產生 GiveBonusCommand
。 此外,藉由將屬性 (attribute) 上的 CanExecute
屬性 (property) 設定為我們想要連結至 ICommand
之 CanExecute
方法的方法名稱,其會產生程式碼來為我們進行此設定。
public partial class EmployeeViewModel : ObservableObject
{
public EmployeeViewModel(Employee model)
{
}
[RelayCommand(CanExecute = nameof(GiveBonusCanExecute))]
void GiveBonus()
{
//logic for giving bonus
}
bool GiveBonusCanExecute()
{
//logic for deciding if "give bonus" button should be enabled.
return true;
}
}
MVVM 工具組也會處理 async
方法,這在 .NET 程式設計中很常見。
具有參數的命令
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 GiveBonusCommand}" CommandParameter="25" />
此命令必須解譯並轉換該字串參數。 有許多方法可提供強型別參數。
請使用 XAML 元素,而不是使用屬性語法來定義
CommandParameter
。<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}"> <Button.CommandParameter> <x:Int32>25</x:Int32> </Button.CommandParameter> </Button>
將
CommandParameter
繫結至正確類型的執行個體。如果
CommandParameter
繫結至不正確的類型,請套用轉換器,將值轉換成正確的類型。