1

我正在使用 MVVM 模式为 Windows Store 和 Windows Phone 8 开发同一个应用程序的两个版本。每个应用程序都有自己的视图。Model 和 ViewModel 在 Portable Class Libraray 中共享。我正在使用 TPL 任务在模型中执行异步操作。由于 Portable Class Library 的限制,我不能使用 async 和 await 关键字。

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

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

我尝试了两种不同的方法:

一个(不起作用)

在开始操作之前保存对调度程序的引用

TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();

然后将其传递给ContinueWith方法。

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

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

第二

现在我尝试使用

Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler);

因为我不能直接使用 PCL 中的 Dispatcher,所以我将对它的引用(隐藏在包装器中)传递给模型中的几乎每个对象。(就像在这个答案中一样)这终于奏效了,但它非常复杂和丑陋。

所以我的问题是:

  1. 哪种方法可以返回到 Portable Class Libraray 中的 UI 线程?
  2. 我在尝试中的错误是什么?

我知道这个话题已经有很多问题了,但不幸的是,没有什么能真正解决我的问题。

4

1 回答 1

2

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

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

这个Changed函数在 WPF 应用程序下可以正常工作,因为 WPF 会将属性更改排队并在下一次 UI 框架更新时处理它们。由于 Windows Phone 没有,我们需要建立一个模式来在运行时改变这种行为。我创建了一个名为Dispatcher的属性,我允许在运行时设置它。我所有的广播现在都从Changed 更改Dispatcher

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

所以现在我们可以在运行时在我们的 Windows Phone 应用程序中更改Dispatcher 。我们需要编写一个函数来推迟更改,直到 UI 线程处于活动状态以广播更改。我在一个扩展中这样做了,所以在 ViewModel 上附加 UI 线程安全会更容易一些。运行时更改将简单地使用 Windows Phone Dispatcher在 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 组件,例如视图。现在,您真正需要做的就是在视图模型上调用attach 。

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

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

于 2013-03-16T11:42:20.713 回答