如何解决Dispatcher 和 RaiseCanExecuteChanged() 阻塞
我一直致力于设置一些 WPF UserControl 以异步加载它们的外部 API 数据,并且大部分进展顺利 - 我创建的模式和基类适用于 99% 的用例。但是,特别是一项控制和服务让我很不舒服。
示例中的外部 API 服务已经过验证、测试并在应用程序的其他异步部分工作,因此真正的“新行为”是从非异步上下文调用服务,并使用调度程序来传送结果返回到 UI 线程。更具体地说,此模式有效,数据正在 UI 中更新,但 CanExecute 方法从未被更新。
查看模型
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);
dispatcher.Currentdispatcher.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 runcommand()
{
//anotherExampleService.DoWork(CurrentAPIResult);
}
问题是在设置 Canruncommand
后永远不会调用 CurrentAPIResult
,而且我假设在调用 ExampleCommand.RaiseCanExecuteChanged()
时有一些比我理解的更微妙的东西。
如果我在 CurrentAPIResult
setter 中设置断点,我可以跳过那个调用,所以我知道它正在被调用,就好像它仍然被非 UI 线程调用一样。
WPF .Net 框架 4.8,MVVMLight
我尝试了 dispatcher 方法的各种组合 - BeginInvoke
、InvokeAsync
等……但都没有成功。
我尝试创建一个单独的方法来在 dispatched 调用中调用,但这也不起作用。
private void Update(APIResult currentAPIResult,int id)
{
CurrentAPIResult = currentAPIResult;
_lastIdLoaded = id;
}
所以我希望我只是在这里忽略了一些相当简单的事情,并且有人可以帮助我。
编辑:
我发现现有的例子不够完整,所以我只想用一个有效的例子来编辑这个问题,这样任何登陆这里的人都可以看到这个模式的正确实现。
此线程提供了有关 dispatcher.Currentdispatcher
和 Application.Current.dispatcher
之间区别的更多信息:
Dispatcher.CurrentDispatcher vs. Application.Current.Dispatcher
工作示例:
查看模型
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.Invoke(() =>
{
CurrentAPIResult = apiResult;
_lastIdLoaded = id;
}
}
private APIResult _currentAPIResult;
public APIResult CurrentAPIResult
{
get => _currentAPIResult;
set
{
Set(ref _currentAPIResult,Canruncommand));
private bool Canruncommand
{
return CurrentAPIResult != null;
}
public runcommand()
{
//anotherExampleService.DoWork(CurrentAPIResult);
}
我发现一些有用的东西:
Application.Current.dispatcher
位于 System.Windows
命名空间中。
我能够在 LoadInternal
和 CurrentAPIResults
setter 中设置断点并打开 Debug > Windows > Threads
视图,以查看从哪个线程调用 LoadInternal
和随后的 setter,以确认执行的地点。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。