微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Dispatcher 和 RaiseCanExecuteChanged() 阻塞

如何解决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 方法的各种组合 - BeginInvokeInvokeAsync 等……但都没有成功。

我尝试创建一个单独的方法来在 dispatched 调用调用,但这也不起作用。

private void Update(APIResult currentAPIResult,int id)
{
    CurrentAPIResult = currentAPIResult;
    _lastIdLoaded = id;
}

所以我希望我只是在这里忽略了一些相当简单的事情,并且有人可以帮助我。

编辑:

我发现现有的例子不够完整,所以我只想用一个有效的例子来编辑这个问题,这样任何登陆这里的人都可以看到这个模式的正确实现。

此线程提供了有关 dispatcher.CurrentdispatcherApplication.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 命名空间中。

我能够在 LoadInternalCurrentAPIResults setter 中设置断点并打开 Debug > Windows > Threads 视图,以查看从哪个线程调用 LoadInternal 和随后的 setter,以确认执行的地点。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。