【问题标题】:Run code on UI thread in WinRT在 WinRT 中的 UI 线程上运行代码
【发布时间】:2012-05-21 16:24:06
【问题描述】:

如何在 WinRT (Windows 8 Metro) 的 UI 线程上运行代码?

Invoke 方法不存在。

【问题讨论】:

  • 未来读者请注意:请记住,如果您的应用有多个窗口 - 会有多个 UI 线程和调度程序。

标签: .net multithreading windows-8 windows-runtime dispatcher


【解决方案1】:

直接从非UI线程获取CoreWindow更容易。以下代码适用于任何地方,即使 GetForCurrentThread()Window.Current 返回 null

CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    <lambda for your code which should run on the UI thread>);

例如:

CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    () =>
    {
        // Your UI update code goes here!
    });

您需要引用 Windows.ApplicationModel.Core 命名空间:

using Windows.ApplicationModel.Core;

【讨论】:

  • 使用 this 时出现 System.NotImplementedException。我从 UI 线程访问它。
  • 这里从来没有这个异常。它可能来自其他地方,例如在您的块指令中。
  • 当我尝试访问 Dispatcher 时出现此异常。我没有使用 Dispatcher 执行任何代码。
  • 所以CoreApplication.MainView.CoreWindow.Dispatcher 不适合你?尝试其他解决方案时是否有异常? Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher? this.Dispatcher?
  • 我也试过Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher,但这也引发了异常。我猜this.Dispatcher不适用于Windows.Runtime。我已经将它作为单独的question发布。
【解决方案2】:

用途:

从你的 UI 线程,执行:

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;

来自您的背景(非 UI 线程)

dispatcher.RunAsync(DispatcherPriority.Normal, 
    <lambda for your code which should run on the UI thread>);

这应该适用于 CP 和更高版本。

【讨论】:

  • 有没有办法在非 UI 线程上获取调度程序?目前我从 CoreWindow.GetForCurrentThread() 得到 null
  • 没有。调度程序与 UI 线程相关联,因此您需要在 UI 线程上检索调度程序。检索到调度程序后,您可以记住它。如果您在 XAML 应用程序中,那么大多数 UI 对象都有您可以使用的调度程序成员。
  • 那么我的应用程序的哪些部分实际上是在 UI 线程中运行的?我正在使用 FrameWorkView (Windows::ApplicationModel::Core::IFrameworkView),但无法使用从 Run() 方法获得的调度程序。当我尝试通过 RunAsync 创建 MediaElement 时出现 WrongThreadException。
  • 您的原始代码在 UI 线程上运行,任何基于 XAML 的事件回调也是如此)。如果您在 UI 线程中使用“await”关键字,则“await”关键字之后的所有代码都将在 UI 线程上运行。但是,来自其他 WinRT API 的事件回调可能不在 UI 线程上,如果不在,则使用调度程序返回 UI 线程。如果您收到 WrongThreadException,则可能意味着 WinRT API 进入了非 UI 线程。
  • @Larry:你可以从后台线程获取Dispatcher,看这里:stackoverflow.com/a/25760799/543303CoreApplication.MainView.CoreWindow.Dispatcher
【解决方案3】:

用途:

this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Frame.Navigate(typeof(Welcome), this));

它对我有用。

【讨论】:

  • 这实际上并不能保证在 UI 线程上运行它。仅当“this”是 UI 上下文中的对象时才会这样做。
【解决方案4】:

在我看来这是一种更简单的方法。

获取与 UI 关联的 TaskScheduler。

    var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

然后在上面的UISyncContext上启动一个新Task。

    Task.Factory.StartNew(() => { /* Do your UI stuff here; */}, new System.Threading.CancellationToken(), TaskCreationOptions.PreferFairness, UISyncContext);

【讨论】:

  • 仅供参考:我收到 InvalidOperationException “当前 SynchronizationContext 可能无法用作 TaskScheduler”。
  • 我也得到了一个 InvalidOperation
  • 您的 SynchronizationContext 为空。这就是为什么你得到那个例外。我的代码没有错。
【解决方案5】:

DispatcherTimer 也是一个选项。

我将它用于必须在 Xaml-designer 中运行的代码(CoreWindow.Dispatcher,...在 UWP-designer 中不可用)

var localTimer = new DispatcherTimer
{
    Interval = TimeSpan.FromMilliseconds(0)
};
localTimer.Tick += (timer, e) =>
{
    (timer as DispatcherTimer).Stop();
    action();
};
localTimer.Start();

免责声明:
我应该注意,如果其他所有方法都失败了,这应该是最后的选择。

【讨论】:

    【解决方案6】:

    在 UWP 上,我在尝试设置 CaptureElement 控件(在 XAML 中定义)的 Source 属性时遇到问题,它抱怨在不同的线程准备,即使我试图从被调用的代码中设置它通过 Page_Loaded 事件处理程序。我最终使用它来解决它:

    previewControl.Dispatcher.TryRunAsync(CoreDispatcherPriority.Normal, () => {
       previewControl.Source = _mediaCapture;
    }).GetAwaiter().GetResult();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-02
      • 1970-01-01
      • 2022-11-11
      • 2015-02-17
      • 2018-09-13
      • 1970-01-01
      • 2015-01-25
      • 2020-01-30
      相关资源
      最近更新 更多