You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用IAsyncRelayCommand替代ICommand并实现ChangeCanExecute等效功能?

用IAsyncRelayCommand替代ICommand并实现CanExecute更新

问题背景

需要将ViewModel中的PreviousPageCommand和NextPageCommand从ICommand替换为IAsyncRelayCommand,同时保留原有的ChangeCanExecute()功能,确保命令的可执行状态能正确更新。

原ViewModel代码

[ObservableObject]
public partial class MotorcycleListViewModel(IMotorcycleService motorcycleService)
{
    #region life cycle commands
    public IAsyncRelayCommand AppearingCommand => new AsyncRelayCommand(OnAppearingAsync);
    public IAsyncRelayCommand DisappearingCommand => new AsyncRelayCommand(OnDisappearingAsync);
    #endregion

    #region paging commands
    public ICommand PreviousPageCommand { get; private set; }
    public ICommand NextPageCommand { get; private set; }
    #endregion

    [ObservableProperty]
    private ObservableCollection<MotorcycleModel> motorcycles;

    private int page = 1;
    private bool isLoading = false;
    private bool hasNextPage = true;

    private async Task OnAppearingAsync()
    {
        PreviousPageCommand = new Command(async () => await OnPreviousPageAsync(), () => page > 1 && !isLoading);
        NextPageCommand = new Command(async () => await OnNextPageAsync(), () => !isLoading && hasNextPage);

        await LoadMotorcycles();
    }

    private async Task OnDisappearingAsync()
    { }

    private async Task OnPreviousPageAsync()
    {
        if (isLoading) return;

        page = page <= 1 ? 1 : --page;
        await LoadMotorcycles();
    }

    private async Task OnNextPageAsync()
    {
        if (isLoading) return;

        page++;
        await LoadMotorcycles();
    }

    private async Task LoadMotorcycles()
    {
        isLoading = true;

        var result = await motorcycleService.GetPagedAsync(page);

        if (result.IsError)
        {
            await Application.Current.MainPage.DisplayAlert("Error", "Motorcycles not loaded!", "OK");
            return;
        }

        hasNextPage = !(result.Value.Count < 2);

        Motorcycles = new ObservableCollection<MotorcycleModel>(result.Value);

        isLoading = false;

        ((Command)PreviousPageCommand).ChangeCanExecute();
        ((Command)NextPageCommand).ChangeCanExecute();
    }
}

解决方案步骤

1. 修改命令类型声明

将分页命令的类型从ICommand改为IAsyncRelayCommand:

#region paging commands
public IAsyncRelayCommand PreviousPageCommand { get; private set; }
public IAsyncRelayCommand NextPageCommand { get; private set; }
#endregion

2. 初始化AsyncRelayCommand

在OnAppearingAsync方法中,用AsyncRelayCommand替代原有的Command,直接传入异步方法和可执行条件:

private async Task OnAppearingAsync()
{
    PreviousPageCommand = new AsyncRelayCommand(OnPreviousPageAsync, () => page > 1 && !isLoading);
    NextPageCommand = new AsyncRelayCommand(OnNextPageAsync, () => !isLoading && hasNextPage);

    await LoadMotorcycles();
}

3. 替换CanExecute更新方法

将原有的ChangeCanExecute()调用替换为IAsyncRelayCommand的NotifyCanExecuteChanged()方法,该方法用于手动触发命令可执行状态的更新:

private async Task LoadMotorcycles()
{
    isLoading = true;

    var result = await motorcycleService.GetPagedAsync(page);

    if (result.IsError)
    {
        await Application.Current.MainPage.DisplayAlert("Error", "Motorcycles not loaded!", "OK");
        return;
    }

    hasNextPage = !(result.Value.Count < 2);

    Motorcycles = new ObservableCollection<MotorcycleModel>(result.Value);

    isLoading = false;

    // 替换原ChangeCanExecute调用,手动通知命令更新可执行状态
    PreviousPageCommand.NotifyCanExecuteChanged();
    NextPageCommand.NotifyCanExecuteChanged();
}

(可选)优化:自动更新CanExecute状态

如果希望无需手动调用NotifyCanExecuteChanged(),可以将page、isLoading、hasNextPage这些私有字段改为ObservableProperty,借助CommunityToolkit.Mvvm的自动通知机制,当属性值变化时,命令会自动检测可执行条件:

[ObservableProperty]
private int page = 1;

[ObservableProperty]
private bool isLoading = false;

[ObservableProperty]
private bool hasNextPage = true;

此时命令的初始化可以直接使用生成的属性(注意属性名首字母大写):

PreviousPageCommand = new AsyncRelayCommand(OnPreviousPageAsync, () => Page > 1 && !IsLoading);
NextPageCommand = new AsyncRelayCommand(OnNextPageAsync, () => !IsLoading && HasNextPage);

同时可以删除LoadMotorcycles中的NotifyCanExecuteChanged()调用,因为属性变化会自动触发命令的可执行状态更新。

修改后的完整ViewModel代码(基础版本)

[ObservableObject]
public partial class MotorcycleListViewModel(IMotorcycleService motorcycleService)
{
    #region life cycle commands
    public IAsyncRelayCommand AppearingCommand => new AsyncRelayCommand(OnAppearingAsync);
    public IAsyncRelayCommand DisappearingCommand => new AsyncRelayCommand(OnDisappearingAsync);
    #endregion

    #region paging commands
    public IAsyncRelayCommand PreviousPageCommand { get; private set; }
    public IAsyncRelayCommand NextPageCommand { get; private set; }
    #endregion

    [ObservableProperty]
    private ObservableCollection<MotorcycleModel> motorcycles;

    private int page = 1;
    private bool isLoading = false;
    private bool hasNextPage = true;

    private async Task OnAppearingAsync()
    {
        PreviousPageCommand = new AsyncRelayCommand(OnPreviousPageAsync, () => page > 1 && !isLoading);
        NextPageCommand = new AsyncRelayCommand(OnNextPageAsync, () => !isLoading && hasNextPage);

        await LoadMotorcycles();
    }

    private async Task OnDisappearingAsync()
    { }

    private async Task OnPreviousPageAsync()
    {
        if (isLoading) return;

        page = page <= 1 ? 1 : --page;
        await LoadMotorcycles();
    }

    private async Task OnNextPageAsync()
    {
        if (isLoading) return;

        page++;
        await LoadMotorcycles();
    }

    private async Task LoadMotorcycles()
    {
        isLoading = true;

        var result = await motorcycleService.GetPagedAsync(page);

        if (result.IsError)
        {
            await Application.Current.MainPage.DisplayAlert("Error", "Motorcycles not loaded!", "OK");
            return;
        }

        hasNextPage = !(result.Value.Count < 2);

        Motorcycles = new ObservableCollection<MotorcycleModel>(result.Value);

        isLoading = false;

        PreviousPageCommand.NotifyCanExecuteChanged();
        NextPageCommand.NotifyCanExecuteChanged();
    }
}

内容的提问来源于stack exchange,提问作者Wasyster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:43:17