如何在异步操作后返回UI线程

本文关键字:返回 UI 线程 异步操作 | 更新日期: 2023-09-27 18:03:30

我正在使用MVVM模式为Windows Store和Windows Phone 8开发同一应用程序的两个版本。每个应用程序都有自己的视图。模型和视图模型在可移植类库中共享。我通过使用TPL任务在模型内进行异步操作。由于可移植类库的限制,我不能使用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. 在可移植类库中返回UI线程的推荐方法是什么?
  2. 我在尝试One时犯了什么错误?

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

如何在异步操作后返回UI线程

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应用程序中附加一个视图模型的新实例。如果还有其他问题,请告诉我,祝你好运!