Richtiger Weg, um den CoreDispatcher in einer Windows Store-App zu erhalten

83

Ich erstelle eine Windows Store-App und habe Code, der im UI-Thread veröffentlicht werden muss.

Dafür möchte ich den CoreDispatcher abrufen und zum Posten des Codes verwenden.

Es scheint, dass es dafür einige Möglichkeiten gibt:

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

// Second way
Window.Current.Dispatcher;

Ich frage mich, welches richtig ist? oder ob beide gleichwertig sind?

Lysergsäure
quelle
3
Beide sind Art von richtig, aber es wird null , wenn Sie sich nicht von etwas zugreifen , die bereits hat Zugriff auf den Dispatcher. Wenn Sie es beispielsweise in einem ViewModel oder Controller verwenden möchten, müssen Sie den Dispatcher im Allgemeinen als statische Eigenschaft in Ihrem App.xaml.cs- oder IOC-Controller speichern und auf der ersten Seite festlegen Du hast Last.
Nate Diamond

Antworten:

147

Dies ist der bevorzugte Weg:

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

Dies hat den Vorteil, dass es die Hauptleitung erhält CoreApplicationViewund somit immer verfügbar ist. Weitere Details hier .

Es gibt zwei Alternativen, die Sie verwenden können.

Erste Alternative

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

Dadurch wird die aktive Ansicht für die App abgerufen, Sie erhalten jedoch null , wenn keine Ansichten aktiviert wurden. Weitere Details hier .

Zweite Alternative

Window.Current.Dispatcher

Diese Lösung funktioniert nicht, wenn sie von einem anderen Thread aufgerufen wird, da sie anstelle des UI-Dispatchers null zurückgibt . Weitere Details hier .

MAXE
quelle
Ich habe dies versucht, aber wenn ich den Code verfolge, wird der Delegatencode immer noch in einem Worker-Thread ausgeführt und nicht im "Haupt-Thread".
Robert Oschler
3
Bitte beachten Sie, dass (zumindest in Windows 8.1) DispatcherPriority jetzt CoreDispatcherPriority
Illidan
2
Dies würde funktionieren, solange wir eine einzelne ASTA (Application Single-Threaded Apartment) haben. Für den Fall, dass wir die Funktion "Ziel freigeben" einführen, gibt es mehrere ASTAs (jede mit einem eigenen Dispatcher). Und dann kann CoreApplication.MainView null sein (weil seine ASTA noch nicht initialisiert ist). Sei vorsichtig!
Yury Schkatula
Ich habe gesehen, dass CoreApplication.MainView dazu führt, dass mein Programm hängt, wenn es von einem Nicht-UI-Thread aufgerufen wird. Ich musste den CoreApplication.MainView.CoreWindow.Dispatcher beim Start verstauen, um später darauf zugreifen zu können.
sjb-sjb
15

Für alle, die C ++ / CX verwenden

Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
    CoreDispatcherPriority::Normal,
    ref new Windows::UI::Core::DispatchedHandler([this]()
{
    // do stuff
}));
Brett Pennings
quelle
1
"Zum Erstellen und Verwenden von Windows Runtime-APIs mit C ++ gibt es C ++ / WinRT. Dies ist der von Microsoft empfohlene Ersatz für die Windows Runtime C ++ - Vorlagenbibliothek (WRL) und C ++ / CX." C ++ / WinRT
Richard Chambers
2
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
            CoreDispatcherPriority.Normal,
            () => { // your code should be here});
Apramc
quelle
1

Obwohl dies ein alter Thread ist, wollte ich auf ein mögliches Problem aufmerksam machen, auf das Entwickler stoßen könnten, das mich betroffen hat und das Debuggen in großen UWP-Apps äußerst schwierig machte. In meinem Fall habe ich den folgenden Code aus den obigen Vorschlägen im Jahr 2014 überarbeitet, wurde jedoch gelegentlich von gelegentlichen zufälligen Einfrierungen der App geplagt.

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

Aus dem oben Gesagten hatte ich eine statische Klasse verwendet, um den Aufruf des Dispatchers in der gesamten Anwendung zu ermöglichen - wobei ein einzelner Aufruf möglich war. In 95% der Fälle war auch durch QS-Regression alles in Ordnung, aber Kunden meldeten ab und zu ein Problem. Die Lösung bestand darin, den folgenden Aufruf einzuschließen und keinen statischen Aufruf auf den tatsächlichen Seiten zu verwenden.

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

            });

Dies ist nicht der Fall, wenn ich sicherstellen muss, dass der UI-Thread von App.xaml.cs oder meinem Singleton NavigationService aufgerufen wurde, der das Pushing / Popping auf den Stack handhabte. Der Dispatcher verlor anscheinend den Überblick darüber, welcher UI-Thread aufgerufen wurde, da jede Seite einen eigenen UI-Thread hat, wenn auf dem Stack verschiedene Nachrichten vom MessageBus ausgelöst wurden.

Ich hoffe, dies hilft anderen, die möglicherweise betroffen sind, und ich denke, dass jede Plattform ihren Entwicklern einen Service bieten würde, indem sie ein vollständiges Projekt veröffentlicht, das die Best Practices abdeckt.

Dave Friedel
quelle
0

Eigentlich würde ich etwas in dieser Richtung vorschlagen:

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

Auf diese Weise werden die Dispatcher nicht verwirrt, wenn Sie eine andere Ansicht / ein anderes Fenster geöffnet haben ...

Dieses kleine Juwel prüft, ob es überhaupt ein Fenster gibt. Wenn keine vorhanden ist, verwenden Sie den Dispatcher von MainView. Wenn es eine Ansicht gibt, verwenden Sie den Dispatcher.

JH
quelle