如何用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
相关产品推荐
相关产品推荐

