1

我正在使用MVVM模式为Windows Store和Windows Phone 8开发同一应用程序的两个版本。每个应用都有自己的视图。 Model和ViewModel在Portable Class Libraray中共享。我使用TPL任务在模型中执行异步操作。由于可移植类库的限制,我无法使用async和await关键字。如何在异步操作后返回到UI线程

任务完成后,我想回到UI线程并更新一些属性(这会导致ViewModel和View也更新)。

在我看来,这似乎是一个很常见的情况,所以我有点困惑,为什么它变得如此艰难。

我尝试了两种不同的方法:在开始操作

TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

然后将它传递给ContinueWith方法之前

一个(不工作)

保存到调度一个参考。

myTask.ContinueWith(t => myTaskCompleted(t.Result), scheduler); 

在我看来,这似乎是一个很好的解决方案,但不起作用。 myTaskCompleted仍然在不同的线程中执行。

现在我试图使用

Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler); 

因为我不能从PCL使用分派器直接,我传递给它(隐藏在一个包装)的引用,以几乎所有的对象在模型中。 (就像在这answer)这最后的作品,但它是相当复杂和丑陋。

所以我的问题是:

  1. 这是建议的方式要回UI线程便携类Libraray里面?
  2. 我尝试的错误是什么一个

我知道那里已经有很多关于这个话题的问题,但不幸的是没有真正解决我的问题。

+7

第一次尝试时代码的上下文是什么?请记住,如果您目前不在UI线程中,那么TaskScheduler.FromCurrentSynchronizationContext()不会获取UI线程的上下文。如果您已经在后台线程中,则为时已晚,您需要早一点抓取它。 – Servy 2013-03-12 21:07:23

+0

Duplicate:http://stackoverflow.com/questions/11258164/portable-class-library-equivalent-of-dispatcher-invoke-or-dispatcher-runasync(至少他们看起来是一样的我) – 2013-03-12 21:07:53

+4

你看着[ Microsoft.Bcl.Async(http://nuget.org/packages/Microsoft.Bcl.Async/)?你的第一种方法对我来说是正确的 - 除非@Servy建议你的'FromCurrentSynchronizationContext'不在UI线程上运行。 – 2013-03-12 21:13:19

回答

2

TPL将使用线程池中的一个线程,并且UI线程是不在线程池中的“主线程”,永远不可用于运行任务。使用ContinueWith函数将从线程池中获取另一个线程以执行您的代码。您遇到的问题的核心在于Windows Phone不会排列属性更改,并会直接尝试更新视图。在代码中的某处,您应该有一个更改函数,它可以广播属性更改。我会用我的:

public void Changed(string Key) { 
    // Check if the property changed has subscribers. 
    if (PropertyChanged != null) { 
     // Invoke the property changed. 
     PropertyChanged(this, new PropertyChangedEventArgs(Key)); 
    } 
} 

改变功能将运行在一个WPF应用程序很好,因为WPF将排队性质的变化,并处理他们在接下来的UI框架的更新。由于Windows Phone没有,我们需要建立一个模式来在运行时改变这种行为。我做了一个名为调度程序的属性,我允许在运行时设置它。我所有的广播已从更改为改为调度员

private Action<string> _Dispatcher; 
public Action<string> Dispatcher { 
    get { 
     if (_Dispatcher == null) { 
      return Changed; 
     } 
     return _Dispatcher; 
    } 
    set { 
     _Dispatcher = value; 
    } 
} 

所以现在我们可以在运行时在我们的Windows Phone应用程序更改调度。我们需要编写一个推迟更改的函数,直到UI线程处于活动状态才能广播更改。我在一个扩展中做了这个,所以在ViewModel上附加UI线程安全性会更容易一些。运行时更改将简单地使用Windows Phone 调度程序在UI线程上调度广播。的实现如下:

public static void Attach(this ViewModelStore ViewModelStore, DependencyObject DependencyObject) { 
    // Set the changed event dispatcher. 
    ViewModelStore.Dispatcher = (Key) => { 
     // Begin invoking of an action on the UI dispatcher. 
     DependencyObject.Dispatcher.BeginInvoke(() => { 
      // Raise the changed event. 
      ViewModelStore.Changed(Key); 
     }); 
    }; 
} 

ViewModelStore是我一直在用我所有的视图模型的通用类,所以这个功能可以让我的线程安全的广播机构附接到所有的视图模型。 DependencyObject是一个UI组件,例如视图。现在,您真正需要做的就是在视图模型上调用附加

ProviderViewModel.Attach(this); // This is inside a Page. 

所有广播都没有委托给UI线程,并调用了UI进入的下一帧,并相应地更新了所有内容。您不必担心这样的线程安全性,但您需要记住在您的Windows Phone应用程序中附加视图模型的新实例。让我知道是否还有其他问题,祝你好运!

相关问题