C# 从WinRT中的线程更新UI

C# 从WinRT中的线程更新UI,c#,windows-runtime,C#,Windows Runtime,自从Windows8用户预览版几天前发布以来,我正在用C#开发新的WinRT(用于Metro应用程序),并将我自己编写的IRC类移植到新的线程和网络中 问题是:我的类正在运行从服务器接收消息的线程。如果发生这种情况,线程将进行一些解析,然后触发一个事件来通知应用程序。然后,订阅的函数“应该”更新UI(文本块) 这就是问题所在,线程无法更新UI,使用.NET4.0的调用程序方法似乎不再可能。有没有一种新的解决方法或者更好的方法来更新UI?如果我尝试从事件订阅服务器更新UI,我将得到以下异常: 该应

自从Windows8用户预览版几天前发布以来,我正在用C#开发新的WinRT(用于Metro应用程序),并将我自己编写的IRC类移植到新的线程和网络中

问题是:我的类正在运行从服务器接收消息的线程。如果发生这种情况,线程将进行一些解析,然后触发一个事件来通知应用程序。然后,订阅的函数“应该”更新UI(文本块)

这就是问题所在,线程无法更新UI,使用.NET4.0的调用程序方法似乎不再可能。有没有一种新的解决方法或者更好的方法来更新UI?如果我尝试从事件订阅服务器更新UI,我将得到以下异常:

该应用程序调用了一个为 不同的线程(HRESULT的异常:0x8001010E (RPC_E_错误_线程))


在WinRT(以及一般的C#5)中处理此问题的首选方法是使用
async
-
wait

private async void Button_Click(object sender, RoutedEventArgs e)
{
    string text = await Task.Run(() => Compute());
    this.TextBlock.Text = text;
}
这里,
Compute()
方法将在后台线程上运行,当它完成时,该方法的其余部分将在UI线程上执行。同时,UI线程可以自由地执行任何需要的操作(如处理其他事件)

但如果您不想或不能使用
async
,则可以使用
Dispatcher
,使用方式与WPF中类似(尽管不同):

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() => Compute());
}

private void Compute()
{
    // perform computation here

    Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString);
}

private void ShowText(object sender, InvokedHandlerArgs e)
{
    this.TextBlock.Text = (string)e.Context;
}

我想这里有一个更简单的方法

首先使用以下内容捕获您的UI SyncronizationContext:

    var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
运行服务器调用操作或所需的任何其他后台线程操作:

    Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} );
然后在第一步中捕获的UISyncContext上执行UI操作:

    Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext);
我认为“线程池”是推荐的路线

公共静态任务InvokeBackground(Func操作)
{
var tcs=new TaskCompletionSource();
var unused=ThreadPool.RunAsync(异步(obj)=>
{
等待行动();
tcs.TrySetResult(真);
});
返回tcs.Task;
}

这是设计的。线程消耗电池电量。异步IO的新样式是指定continuations。关于这一点有一些报道(教程视频)。再想一想,可能是BackgroundWorker仍然可以工作,这更像是一个线程,而且它还具有编组(您可以将进度更新发送到UI线程)。非常感谢,您的第二个想法为我解决了这个问题,而无需重写整个类:),对于第二个示例,我强烈建议使用lambda而不是ShowText方法,它允许您避免使用“e.Context”。调用(CoreDispatcherPriority.Normal,(s,e)=>{this.TextBlock.Text=resultString},this,null);如果代码不在页面中怎么办?我们如何从BG线程获取调度程序?@Grigory您可以尝试使用
Window.Current.dispatcher
,但我不确定这是否有效。如果没有,您必须以某种方式将
调度程序
传递给在BG线程上执行的方法。是的,Window.Current返回null。似乎传递dispatcher或SynchronizationContext是唯一的解决方案。那么,WINRT对“如何从某个随机后台线程获取UI上下文?”的回答除了修改所有接口以在UI上下文中传递之外,似乎没有简单的答案,这是真的吗?如果您的后台线程对象是从UI线程创建的(因此您可以在构造函数中捕获UISyncContext),Deeb的上述回答似乎是有效的。但是如果它不是在UI线程上创建的呢?我遗漏了什么,为什么没有一个大脑死亡的方法来可靠地从任何随机的背景线程中获取UI上下文?这对我来说不起作用。我得到一个InvalidOp异常:
当前SynchronizationContext不能用作TaskScheduler。
我正在Win SDK 8上为MS Surface开发
public static Task InvokeBackground(Func<Task> action)
    {
        var tcs = new TaskCompletionSource<bool>();

        var unused = ThreadPool.RunAsync(async (obj) =>
        {
            await action();
            tcs.TrySetResult(true);
        });

        return tcs.Task;
    }