如何在异步方法中以同步方式更新UI线程上的元素?

时间:2012-12-05 08:59:57

标签: c# async-await windows-phone-8 portable-class-library

我正在开发一个应用程序,并尝试做一切美好和最佳实践;这是没有执行我想要的代码:

async public Task DoStuff()
{
    DispatcherAdapter.Current.BeginInvoke(() => { myObject.SomeProperty = "new value"; });
    await DatabaseAdapter.Current.SaveItem(TableName, myObject);
}

上面的代码没有使用更新的属性值将项目保存到数据库,我想这是因为在我等待的异步方法完成其工作之前,BeginInvoke()不一定完成。 DispatcherAdapter只在后台使用Deployment.Current.Dispatcher,如下所示:

    public override void BeginInvoke(Action a)
    {
        Deployment.Current.Dispatcher.BeginInvoke(a);
    }

这是我所看到的,但不起作用:

  • 等待BeginInvoke()调用 - 因为它没有返回Task而无法正常工作,并且它仍然向下钻取,无法判断Dispatcher.BeginInvoke何时实际完成
  • trying to work with the DispatcherOperation object returned from BeginInvoke() - 正常.NET中可用的已完成事件不适用于Windows Phone 8
  • using SynchronizationContext.Send() - DispatcherSynchronizationContext.Current始终为null,因此不起作用...请参阅下面的无关代码:

    public async override Task Invoke(Action a)
    {
        await Task.Factory.StartNew(() => { DispatcherSynchronizationContext.Current.Send(new SendOrPostCallback(delegate { a.Invoke(); }), null); });
    }
    

基本上,如何避免无效的跨线程访问并确保代码在Windows Phone 8中的异步方法中以特定顺序运行?我想我可以移动.SaveItem进入BeginInvoke ...但感觉就像一个骗子出路,我仍然不能等待BeginInvoke调用(这使得它在异步方法中毫无意义)并且它将是很高兴知道我应该做什么/其他人在这种情况下做了什么。

2 个答案:

答案 0 :(得分:2)

最简单的方法是从UI线程调用async方法。然后你可以这样做:

public async Task DoStuffAsync()
{
  myObject.SomeProperty = "new value";
  await DatabaseAdapter.Current.SaveItem(TableName, myObject);
}

如果您的async方法需要在后台线程上执行某些操作,请将其包装在Task.Run中。

答案 1 :(得分:1)

我最终创建了自己的DispatcherSynchronizationContext,一切似乎都有效:

    public async override Task Invoke(Action a)
    {
        DispatcherSynchronizationContext dsc = new DispatcherSynchronizationContext(Deployment.Current.Dispatcher);
        await Task.Factory.StartNew(() => { dsc.Send(new SendOrPostCallback(delegate { a.Invoke(); }), null); });
    }

我仍然不确定这是否是这样做的推荐方法,或者以这种方式使用DispatcherSynchronizationContext有任何问题...但是我可以等待这个影响UI线程的帮助方法,所以这是一个启动...