为何进入任务方法后,AsyncRelayCommand仍显示未运行?
核心原因
你的问题出在AsyncRelayCommand的执行时序和IsBusy触发时机的不匹配上:
WCT的AsyncRelayCommand在执行ExecuteAsync时,会先调用你传入的Pump方法,等Pump执行到第一个await(也就是await foreach)返回Task后,才会把这个Task赋值给ExecutionTask,同时更新IsRunning为true并触发属性变更。
但你的Pump方法一开始就调用了OnPropertyChanged(nameof(IsBusy)),这时候AsyncRelayCommand还没完成ExecutionTask的赋值和IsRunning的更新,所以:
IsBusy属性读取StartSyncCommand.IsRunning时,得到的是初始的false- 此时在
Pump内部访问StartSyncCommand.ExecutionTask,自然也是null(或Task.CompletedTask)
额外潜在问题
你的StartSyncCommand初始化代码里,CanExecute委托直接引用了未完成赋值的_StartSyncCommand:
_StartSyncCommand ??= new AsyncRelayCommand(Pump, () => !_StartSyncCommand.IsRunning);
虽然闭包会捕获变量引用,后续能拿到正确值,但如果在初始化完成前(极端场景)调用CanExecute,会触发NullReferenceException,存在隐患。
方案一:移除手动触发,依赖命令状态自动更新
直接订阅AsyncRelayCommand的PropertyChanged事件,当IsRunning变化时自动触发IsBusy的属性变更,不需要在Pump里手动调用OnPropertyChanged:
private AsyncRelayCommand _StartSyncCommand; public AsyncRelayCommand StartSyncCommand { get { if (_StartSyncCommand == null) { AsyncRelayCommand command = null; // 用局部变量捕获命令实例,避免null引用和递归问题 command = new AsyncRelayCommand(Pump, () => !command.IsRunning); // 订阅命令的属性变更,同步更新IsBusy command.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(AsyncRelayCommand.IsRunning)) { OnPropertyChanged(nameof(IsBusy)); } }; _StartSyncCommand = command; } return _StartSyncCommand; } } // 保留原来的IsBusy定义 public bool IsBusy => StartSyncCommand.IsRunning;
然后删除Pump方法里的OnPropertyChanged(nameof(IsBusy));这一行。
方案二:延迟触发IsBusy的属性变更
在Pump方法里先await Task.Yield(),让AsyncRelayCommand先完成状态更新,再触发IsBusy的变更:
private async Task Pump(CancellationToken token) { try { // 让出当前线程,让AsyncRelayCommand完成ExecutionTask赋值和IsRunning更新 await Task.Yield(); OnPropertyChanged(nameof(IsBusy)); await foreach (var item in applicationService.FetchItems()) { token.ThrowIfCancellationRequested(); ... } } catch(Exception ee) { ... } finally { ... } }
同时记得修复StartSyncCommand里CanExecute的潜在问题(用局部变量捕获命令实例)。
方案三:直接绑定命令的IsRunning到UI
如果UI只需要显示忙碌状态,可以跳过IsBusy中间层,直接在XAML里绑定StartSyncCommand.IsRunning,这样彻底避免时序问题:
<Window Cursor="{Binding StartSyncCommand.IsRunning, Converter={StaticResource BooleanToCursorConverter}}">
内容的提问来源于stack exchange,提问作者dotNET

