获取Windows商店应用程序中的CoreDispatcher的正确方法

85

我正在开发一个Windows Store应用程序,并且有一些需要发布到UI线程的代码。

为此,我想获取CoreDispatcher并使用它来发布代码。

看起来有几种方法可以这样做:

// First way
Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.Dispatcher;

// Second way
Window.Current.Dispatcher;

我想知道哪一个是正确的?还是两者都等价?


3
两种方法都有一定的正确性,但如果您没有从已经具有访问Dispatcher权限的内容中访问它,则会为空。如果您想在 ViewModel 或 Controller 中使用它,那么通常需要将 Dispatcher 存储为静态属性在 App.xaml.cs 或 IOC 控制器中,并且要从第一个加载的页面上设置它。 - Nate Diamond
5个回答

155

这是首选的方式:

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

它的优点在于可以获取主要的 CoreApplicationView,因此始终可用。更多详细信息请参见此处

有两种替代方案可供使用。

第一个替代方案

Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.Dispatcher

这将获取应用程序的活动视图,但如果没有激活的视图,则会返回null。更多详情请查看此处

第二种选择

Window.Current.Dispatcher

当从另一个线程调用此解决方案时,它将返回null而不是UI Dispatcher,因此无法正常工作。更多详细信息请参见此处


我尝试过这个,但是当我跟踪代码时,委托代码仍在工作线程上执行,而不是“主线程”。 - Robert Oschler
3
请注意,在 Windows 8.1 中,DispatcherPriority 现在被称为 CoreDispatcherPriority。 - Illidan
2
只要我们有单一的ASTA(应用程序单线程公寓),这个会起作用。如果我们引入“共享目标”功能,那么就会有多个ASTA(每个都有自己的调度程序)。然后CoreApplication.MainView可能为空(因为它的ASTA尚未初始化)。请注意! - Yury Schkatula
我曾经看到CoreApplication.MainView从非UI线程调用时导致我的程序挂起。为了以后访问它,我不得不在启动时存储了CoreApplication.MainView.CoreWindow.Dispatcher。 - sjb-sjb
只要使用"discard operator",这段代码在UWP应用中对我有效。 _ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => someMethod("abc")); - Gregory Bologna
1
@GregoryBologna 有趣...但我认为你的问题只是语法问题...任何lambda表达式都可以使用{}形式:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions - MAXE

16

对于任何使用C++/CX的人

Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
    CoreDispatcherPriority::Normal,
    ref new Windows::UI::Core::DispatchedHandler([this]()
{
    // do stuff
}));

1
针对使用C++撰写和消费Windows Runtime API,Microsoft提供了C++/WinRT。这是替代Windows Runtime C++模板库(WRL)和C++/CX的推荐选择。C++/WinRT - Richard Chambers

2
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
            CoreDispatcherPriority.Normal,
            () => { // your code should be here});

1

虽然这是一个旧线程,但我想引起开发人员注意可能会影响我并使大型UWP应用程序难以调试的潜在问题。在我的情况下,我在2014年从上面的建议中重构了以下代码,但偶尔会遇到随机性的应用程序冻结。

public static class DispatcherHelper
{
    public static Task RunOnUIThreadAsync(Action action)
    {
        return RunOnUIThreadAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, action);
    }

    public static async Task RunOnUIThreadAsync(Windows.UI.Core.CoreDispatcherPriority priority, Action action)
    {
        try
        {
            await returnDispatcher().RunAsync(priority, () =>
            {
                action();
            });
        }
        catch (Exception ex)
        {
            var noawait = ExceptionHandler.HandleException(ex, false);
        }
    }

    private static Windows.UI.Core.CoreDispatcher returnDispatcher()
    {
        return (Windows.UI.Xaml.Window.Current == null) ?
            CoreApplication.MainView.CoreWindow.Dispatcher :
            CoreApplication.GetCurrentView().CoreWindow.Dispatcher;
    }
}

从上述内容可以看出,我使用了一个静态类来允许在整个应用程序中调用Dispatcher - 从而实现单次调用。在95%的时间里,一切都很好,即使通过QA回归测试,但客户偶尔会报告问题。解决方案是在实际页面中包含以下调用,而不是使用静态调用。
            await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            { 

            });

这不是我需要确保从App.xaml.cs或我的单例NavigationService调用UI线程的情况,后者处理推送/弹出堆栈。调度程序显然会丢失被调用的UI线程,因为每个页面都有自己的UI线程,当堆栈中有多种消息从MessageBus触发时。希望这能帮助其他可能受到影响的人,也是我认为每个平台都可以通过发布涵盖最佳实践的完整项目来为开发人员提供服务的地方。

0
实际上,我会提出类似这样的建议:
return (Window.Current == null) ? 
    CoreApplication.MainView.CoreWindow.Dispatcher : 
    CoreApplication.GetCurrentView().CoreWindow.Dispatcher

这样,如果您打开了另一个视图/窗口,您就不会混淆调度程序...

这个小宝石检查是否有窗口。 如果没有,则使用MainView的Dispatcher。 如果有视图,则使用该视图的Dispatcher。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接