Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何让任务返回到WPF中的UI线程_C#_Wpf_Async Await_Task - Fatal编程技术网

C# 如何让任务返回到WPF中的UI线程

C# 如何让任务返回到WPF中的UI线程,c#,wpf,async-await,task,C#,Wpf,Async Await,Task,背景:我在C#方面比较有经验,但对WPF完全陌生 我有一个WPF应用程序,将在内部用于一些简单的监视。我有一个数据库调用,然后返回的数据显示在树视图中, 当这种情况发生时,在数据返回之前,有一个覆盖显示为“加载…”。我当前的实现如下所示: await WithOverlay("Loading...", async () => { MyControl.Items = await _database.Retrieve(messageSummary.Id); }); private a

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

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

await WithOverlay("Loading...", async () =>
{
    MyControl.Items = await _database.Retrieve(messageSummary.Id);
});
private async Task WithOverlay(string overlayMessage, Func<Task> func)
{
    Overlay.Content = overlayMessage;
    Overlay.Visibility = Visibility.Visible;

    await func();

    Overlay.Visibility = Visibility.Hidden;
}
其中,
WithOverlay
如下所示:

await WithOverlay("Loading...", async () =>
{
    MyControl.Items = await _database.Retrieve(messageSummary.Id);
});
private async Task WithOverlay(string overlayMessage, Func<Task> func)
{
    Overlay.Content = overlayMessage;
    Overlay.Visibility = Visibility.Visible;

    await func();

    Overlay.Visibility = Visibility.Hidden;
}
我的想法是:

  • 创建一个延迟250毫秒的任务,该任务完成后将显示覆盖
  • 等待包装的函数
  • 当包装函数完成时,如果覆盖任务尚未完成,则取消它
  • 隐藏覆盖(无论其当前状态如何)
不幸的是,尽管这不起作用;在第
Overlay.Content=overlayMessage
行中,我得到一个异常“调用线程无法访问此对象,因为它属于另一个线程。”

我怀疑这与任务的同步上下文有关(如果我正确地回忆起我的技术演示),但我不知道如何控制它以获得继续
在同一线程上继续。

您可以将
任务调度器
与继续任务关联,以强制在UI线程上设置
内容
可见性
属性的代理:

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

您可以将
TaskScheduler
与延续任务关联,以强制在UI线程上设置
内容
可见性
属性的代理:

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

谢谢你的回答,@mm8。好的,我是这样完成的,我很想听听你对这是否比你的anwser更糟糕的想法:

    private async Task WithOverlay(string overlayMessage, Func<Task> func)
    {
        var delayTask = Task.Delay(250);
        var wrappedTask = func();

        var completedTask = await Task.WhenAny(delayTask, wrappedTask);

        if (completedTask == delayTask)
        {
            Overlay.Content = overlayMessage;
            Overlay.Visibility = Visibility.Visible;
        }

        await wrappedTask;

        Overlay.Visibility = Visibility.Hidden;
    }
private async Task with overlay(字符串overlayMessage,Func Func)
{
var delayTask=任务延迟(250);
var wrappedTask=func();
var completedTask=wait Task.wheny(delayTask,wrappedTask);
if(completedTask==delayTask)
{
Overlay.Content=overlayMessage;
Overlay.Visibility=可见性.Visibility;
}
等待包装任务;
Overlay.Visibility=Visibility.Hidden;
}
因此,我启动了一个仅延迟250毫秒的任务。我启动包装任务,然后使用
任务查看哪一个首先完成;如果延迟任务先完成,则显示

覆盖层。然后我等待包装任务并隐藏覆盖层。

谢谢你的回答,@mm8。好的,我是这样完成的,我很想听听你对这是否比你的anwser更糟糕的想法:

    private async Task WithOverlay(string overlayMessage, Func<Task> func)
    {
        var delayTask = Task.Delay(250);
        var wrappedTask = func();

        var completedTask = await Task.WhenAny(delayTask, wrappedTask);

        if (completedTask == delayTask)
        {
            Overlay.Content = overlayMessage;
            Overlay.Visibility = Visibility.Visible;
        }

        await wrappedTask;

        Overlay.Visibility = Visibility.Hidden;
    }
private async Task with overlay(字符串overlayMessage,Func Func)
{
var delayTask=任务延迟(250);
var wrappedTask=func();
var completedTask=wait Task.wheny(delayTask,wrappedTask);
if(completedTask==delayTask)
{
Overlay.Content=overlayMessage;
Overlay.Visibility=可见性.Visibility;
}
等待包装任务;
Overlay.Visibility=Visibility.Hidden;
}
因此,我启动了一个仅延迟250毫秒的任务。我启动包装任务,然后使用
任务查看哪一个首先完成;如果延迟任务先完成,则显示

覆盖层。然后我等待wrappedTask并隐藏覆盖层。

您可以尝试使用
Dispatcher
而不是tasks,或者使用
Dispatcher。调用
来进行编组。您必须同时等待
funct
覆盖层Task
,WhenAny()。但我认为使用计时器更干净。这是关于async/Wait的最好解释,它确实帮助了我,也可能帮助了你:@bommelding谢谢你的评论。我很好奇你如何用定时器来完成这项工作,你介意详细说明一下吗?当你完成这项工作后,一个需要260毫秒的数据库调用怎么样?您在这里并没有真正解决任何问题,只是将问题转移了一下。您可以尝试使用
dispatchermer
而不是任务,或者使用
Dispatcher.Invoke
进行编组。您必须同时等待
func
overlytask
,使用whenay()。但我认为使用计时器更干净。这是关于async/Wait的最好解释,它确实帮助了我,也可能帮助了你:@bommelding谢谢你的评论。我很好奇你如何用定时器来完成这项工作,你介意详细说明一下吗?当你完成这项工作后,一个需要260毫秒的数据库调用怎么样?你在这里没有真正解决任何问题,只是把问题转移了一下。你的问题是“如何让任务返回WPF中的UI线程”,这就是我的答案。你的问题是“如何让任务返回WPF中的UI线程”,这就是我的答案。