共用方式為


RelayCommand 屬性

RelayCommand 類型是一個屬性,允許產生批註方法的轉譯命令屬性。 其目的是要完全消除在 viewmodel 中定義包裝私用方法的命令所需的重複使用專案。

注意

若要運作,批注方法必須在部分類別。 如果類型是巢狀的,宣告語法樹狀結構中的所有類型也必須標註為部分。 這樣做會導致編譯錯誤,因為產生器將無法使用要求的命令產生該類型的不同部分宣告。

平臺 API:RelayCommandICommandTaskIRelayCommand<T>IRelayCommandIAsyncRelayCommandIAsyncRelayCommand<T>CancellationToken

運作方式

RelayCommand屬性可用來標註部分類型中的方法,如下所示:

[RelayCommand]
private void GreetUser()
{
    Console.WriteLine("Hello!");
}

其會產生如下的命令:

private RelayCommand? greetUserCommand;

public IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser);

注意

產生的命令名稱將會根據方法名稱來建立。 產生器會使用方法名稱並在結尾附加 「Command」,如果存在,則會移除 「On」 前置詞。 此外,對於異步方法,「Async」後綴也會在「命令」應用程式化之前移除。

命令參數

屬性 [RelayCommand] 支援使用 參數建立方法的命令。 在此情況下,它會自動將產生的命令變更為 IRelayCommand<T> ,改為接受相同類型的參數:

[RelayCommand]
private void GreetUser(User user)
{
    Console.WriteLine($"Hello {user.Name}!");
}

這會導致下列產生的程式代碼:

private RelayCommand<User>? greetUserCommand;

public IRelayCommand<User> GreetUserCommand => greetUserCommand ??= new RelayCommand<User>(GreetUser);

產生的命令會自動使用 自變數的類型作為其類型自變數。

異步命令

此命令 [RelayCommand] 也支援透過 IAsyncRelayCommandIAsyncRelayCommand<T> 介面包裝異步方法。 每當方法傳回 Task 類型時,就會自動處理此動作。 例如:

[RelayCommand]
private async Task GreetUserAsync()
{
    User user = await userService.GetCurrentUserAsync();

    Console.WriteLine($"Hello {user.Name}!");
}

這會導致下列程序代碼:

private AsyncRelayCommand? greetUserCommand;

public IAsyncRelayCommand GreetUserCommand => greetUserCommand ??= new AsyncRelayCommand(GreetUserAsync);

如果方法採用 參數,產生的命令也會是泛型。

當方法具有 CancellationToken時,有一個特殊情況,因為將傳播至 命令以啟用取消。 也就是說,方法如下:

[RelayCommand]
private async Task GreetUserAsync(CancellationToken token)
{
    try
    {
        User user = await userService.GetCurrentUserAsync(token);

        Console.WriteLine($"Hello {user.Name}!");
    }
    catch (OperationCanceledException)
    {
    }
}

會產生將令牌傳遞至包裝方法的產生的命令。 這可讓取用者只呼叫 IAsyncRelayCommand.Cancel 來發出該令牌的訊號,並允許正確停止擱置的作業。

啟用和停用命令

通常,能夠停用命令並稍後讓其狀態失效,並再次檢查它們是否可以執行,這通常很有用。 為了支援此功能, RelayCommand 屬性會 CanExecute 公開 屬性,這個屬性可用來指出目標屬性或方法來評估是否可以執行命令:

[RelayCommand(CanExecute = nameof(CanGreetUser))]
private void GreetUser(User? user)
{
    Console.WriteLine($"Hello {user!.Name}!");
}

private bool CanGreetUser(User? user)
{
    return user is not null;
}

如此一來, CanGreetUser 會在按鈕第一次系結至 UI 時叫用,然後在每次在命令上叫用時 IRelayCommand.NotifyCanExecuteChanged 再次叫用它。

例如,這是命令可以系結至屬性以控制其狀態的方式:

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private User? selectedUser;
<!-- Note: this example uses traditional XAML binding syntax -->
<Button
    Content="Greet user"
    Command="{Binding GreetUserCommand}"
    CommandParameter="{Binding SelectedUser}"/>

在此範例中,產生的 SelectedUser 屬性會在每次其值變更時叫 GreetUserCommand.NotifyCanExecuteChanged() 用 方法。 UI 有控件 Button 系結至 GreetUserCommand,這表示每次引發其 CanExecuteChanged 事件時,都會再次呼叫其 CanExecute 方法。 這會導致評估包裝 CanGreetUser 的方法,這會根據輸入 User 實例(UI 中系結至 SelectedUser 屬性的實例) null 是否傳回按鈕的新狀態。 這表示每當 SelectedUser 變更時, GreetUserCommand 都會根據該屬性是否有值來啟用,這是此案例中所需的行為。

注意

當方法或屬性的CanExecute傳回值已變更時,命令不會自動察覺。 開發人員必須呼叫 IRelayCommand.NotifyCanExecuteChanged 來使命令失效,並要求重新評估連結 CanExecute 的方法,然後更新系結至命令之控件的視覺狀態。

處理並行執行

每當命令為異步時,都可以設定為決定是否允許並行執行。 使用 RelayCommand 屬性時,可以透過 AllowConcurrentExecutions 屬性來設定。 默認值為 false,這表示在執行擱置之前,命令會發出其狀態為停用的訊號。 如果它改為設定為 true,則可以將任意數目的並行調用排入佇列。

請注意,如果命令接受取消令牌,如果要求並行執行,也會取消令牌。 主要差異在於,如果允許並行執行,命令會保持啟用狀態,而且會啟動新的要求執行,而不會等待先前的執行實際完成。

處理異步例外狀況

異步轉送命令處理例外狀況的方式有兩種不同的方式:

  • Await 和 rethrow (default):當命令等候叫用完成時,任何例外狀況自然都會在相同的同步處理內容上擲回。 這通常表示擲回的例外狀況只會損毀應用程式,這是與同步命令一致的行為(擲回例外狀況也會損毀應用程式)。
  • 工作排程器的流程例外狀況:如果命令設定為將例外狀況流向工作排程器,則擲回的例外狀況不會使應用程式當機,而是會透過公開 IAsyncRelayCommand.ExecutionTask 的 和 向上升起 TaskScheduler.UnobservedTaskException而變成可用的例外狀況。 這可啟用更進階的案例(例如讓UI元件系結至工作,並根據作業的結果顯示不同的結果),但正確使用會比較複雜。

默認行為是命令等候並重新擲回例外狀況。 這可以透過 屬性進行 FlowExceptionsToTaskScheduler 設定:

[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task GreetUserAsync(CancellationToken token)
{
    User user = await userService.GetCurrentUserAsync(token);

    Console.WriteLine($"Hello {user.Name}!");
}

在此情況下, try/catch 不需要 ,因為例外狀況不會再當機應用程式。 請注意,這也會導致其他不相關的例外狀況自動重新擲回,因此您應該仔細決定如何處理每個個別案例,並適當地設定其餘的程序代碼。

取消異步操作的命令

異步命令的最後一個選項是要求產生取消命令的能力。 這是 ICommand 包裝異步轉播命令,可用來要求取消作業。 此命令會自動發出其狀態的訊號,以反映它是否可以在任何指定時間使用。 例如,如果連結的命令未執行,它會將其狀態報告為非可執行檔。 這可以如下所示使用:

[RelayCommand(IncludeCancelCommand = true)]
private async Task DoWorkAsync(CancellationToken token)
{
    // Do some long running work...
}

這會導致 DoWorkCancelCommand 也會產生 屬性。 然後,這可以系結至一些其他UI元件,讓用戶輕鬆取消暫止的異步操作。

新增自訂屬性

就像使用 可觀察的屬性一樣,產生 RelayCommand 器也支援所產生屬性的自定義屬性。 若要利用這項功能,您可以直接在屬性清單中透過批注方法使用 [property: ] 目標,而MVVM工具組會將這些屬性轉送至產生的命令屬性。

例如,請考慮像這樣的方法:

[RelayCommand]
[property: JsonIgnore]
private void GreetUser(User user)
{
    Console.WriteLine($"Hello {user.Name}!");
}

這會產生 GreetUserCommand 屬性,並具有 [JsonIgnore] 屬性。 您可以視需要使用以 方法為目標的多個屬性清單,而且所有屬性都會轉送至產生的屬性。

範例

  • 查看 範例應用程式 (適用於多個 UI 架構),以查看 MVVM 工具組的運作情形。
  • 您也可以在單元測試中找到更多範例。