WPF中调用RaiseCanExecuteChanged后CanExecute未触发问题
问题根因
问题核心是Dispatcher使用错误:
Dispatcher.CurrentDispatcher会返回当前执行线程的调度器,在Task.Run创建的后台线程中调用该属性,拿到的是后台线程的Dispatcher,而非WPF主UI线程的调度器。因此看似使用了Invoke调度,实际代码仍在后台线程执行,RaiseCanExecuteChanged触发后无法通知UI层更新命令可用状态。- 替换为
Application.Current.Dispatcher即可解决问题,该属性始终返回WPF应用主UI线程的调度器,通过它Invoke的代码都会切到UI线程执行,此时触发RaiseCanExecuteChanged就能正常调用CanRunCommand,同步更新按钮的可用状态。
正确实现代码
public ControlViewModel(IExternalApiService apiService) { _apiService = apiService; } public void Load(int idToLoad) { if (idToLoad == _lastIdLoaded) return; Task.Run(async () => await InternalLoad(idToLoad)); } public async Task InternalLoad(int id) { var apiResult = await _apiService.SpecificEndpoint.GetByIdAsync(id); // 使用Application.Current.Dispatcher获取UI线程调度器 Application.Current.Dispatcher.Invoke(() => { CurrentAPIResult = apiResult; _lastIdLoaded = id; }); } private APIResult _currentAPIResult; public APIResult CurrentAPIResult { get => _currentAPIResult; set { Set(ref _currentAPIResult, value); ExampleCommand.RaiseCanExecuteChanged(); } } private RelayCommand _exampleCommand; public RelayCommand ExampleCommand => _exampleCommand ?? (_exampleCommand = new RelayCommand(RunCommand, CanRunCommand)); private bool CanRunCommand() { return CurrentAPIResult != null; } public void RunCommand() { //anotherExampleService.DoWork(CurrentAPIResult); }
排查技巧
- 可在
InternalLoad方法和CurrentAPIResult的setter中加断点,通过调试>窗口>线程视图观察代码实际运行的线程,确认是否正确切到了UI线程。 Application.Current.Dispatcher位于System.Windows命名空间下,使用前需确认引用正确。
内容的提问来源于stack exchange,提问作者Andy Stagg
相关产品推荐
相关产品推荐

