在 WindowsStore 应用程序中获取 CoreDispatcher 的正确方法

我正在构建一个 Windows 商店应用程序,我有一些代码需要发布到 UI 线程。

为此,我想恢复 CoreDispatcher 并用它发布代码。

似乎有几种方法可以做到这一点:

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


// Second way
Window.Current.Dispatcher;

我想知道哪一个是正确的? 或者两者是否相等?

44753 次浏览

This is the preferred way:

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

The advantage this has is that it gets the main CoreApplicationView and so is always available. More details here.

There are two alternatives which you could use.

First alternative

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

This gets the active view for the app, but this will give you null, if no views has been activated. More details here.

Second alternative

Window.Current.Dispatcher

This solution will not work when it's called from another thread as it returns null instead of the UI Dispatcher. More details here.

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

For anyone using C++/CX

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

Actually, I would propose something in the line of this:

return (Window.Current == null) ?
CoreApplication.MainView.CoreWindow.Dispatcher :
CoreApplication.GetCurrentView().CoreWindow.Dispatcher

That way, should you have openend another View/Window, you won't get the Dispatchers confused...

This little gem checks whether there is even a Window. If none, use the MainView's Dispatcher. If there is a view, use that one's Dispatcher.

While this is an old thread, I wanted to draw attention to a possible issue developers may run across which impacted me and made it extremely difficult to debug in large UWP apps. In my case, I refactored the following code from the suggestions above back in 2014 but would occasionally be plagued with the occasional app freezes that were random in nature.

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;
}
}

From the above, I had used a static class to allow the calling of the Dispatcher through-out the application - allowing for a single call. For 95% of the time, everything was fine even through QA regression but clients would report an issue every now and then. The solution was to include the call below, not using a static call in the actual pages.

            await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{


});

This is not the case when I need to ensure the UI Thread was called from App.xaml.cs or my Singleton NavigationService which handled pushing/popping on to the stack. The dispatcher apparently was losing track of which UI Thread was called, since each page has it's own UI thread, when the stack had a variety of Messages triggering from the MessageBus.

Hope this helps others that may be impacted and it is also where I think each platform would do a service to their developers by publishing a complete project covering the best practices.