首页 > 解决方案 > 如何让任务返回到 WPF 中的 UI 线程

问题描述

背景:我在 C# 方面相对有经验,但对 WPF 完全陌生。

我有一个 WPF 应用程序,它将在内部用于一些简单的监视。我有一个数据库调用,然后返回的数据显示在树视图中,当发生这种情况时,在数据返回之前有一个覆盖显示“正在加载...”。我当前的实现如下所示:

await WithOverlay("Loading...", async () =>
{
    MyControl.Items = await _database.Retrieve(messageSummary.Id);
});

WithOverlay看起来像这样:

private async Task WithOverlay(string overlayMessage, Func<Task> func)
{
    Overlay.Content = overlayMessage;
    Overlay.Visibility = Visibility.Visible;

    await func();

    Overlay.Visibility = Visibility.Hidden;
}

现在,这工作得很好,但是由于很多时候(取决于用户在应用程序的其他地方查看的确切内容)数据库调用很快就会返回,“正在加载...”覆盖只是显示为令人讨厌的闪烁。这只是一件小事,但它困扰着我,所以我决定我可以通过在覆盖出现之前稍微延迟来解决它;比如说,一刻钟。这是我尝试修改WithOverlay方法:

private async Task WithOverlay(string overlayMessage, Func<Task> func)
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;

    var overlayTask = Task.Delay(250, token).ContinueWith(_ => {
        Overlay.Content = overlayMessage;
        Overlay.Visibility = Visibility.Visible;
    });

    await func();

    if (token.CanBeCanceled) tokenSource.Cancel();

    Overlay.Visibility = Visibility.Hidden;
}

我的想法是:

不幸的是,尽管这不起作用;在线Overlay.Content = overlayMessage我得到异常“调用线程无法访问此对象,因为不同的线程拥有它。'”。

我怀疑这与任务的同步上下文有关(如果我没记错我的技术演示),但我不知道如何控制它以使继续在同一个线程上恢复。

标签: c#wpfasync-awaittask

解决方案


您可以将 aTaskScheduler与延续任务相关联,以强制设置ContentVisibility属性的委托在 UI 线程上设置:

var overlayTask = Task.Delay(250, token).ContinueWith(_ => {
    Overlay.Content = overlayMessage;
    Overlay.Visibility = Visibility.Visible;
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

推荐阅读