Prawidłowy sposób na pobranie CoreDispatchera w aplikacji Sklepu Windows

83

Tworzę aplikację Windows Store i mam kod, który należy opublikować w wątku interfejsu użytkownika.

W tym celu chciałbym pobrać CoreDispatcher i użyć go do wysłania kodu.

Wygląda na to, że można to zrobić na kilka sposobów:

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

// Second way
Window.Current.Dispatcher;

Zastanawiam się, który z nich jest poprawny? lub jeśli oba są równoważne?

kwas lizergowy
źródło
3
Oba są w pewnym sensie poprawne, ale będą zerowe, jeśli nie uzyskasz do niego dostępu z czegoś, co ma już dostęp do Dispatchera. Jeśli chcesz go użyć, powiedzmy, w modelu widoku lub kontrolerze, musisz zapisać Dispatcher, ogólnie jako właściwość statyczną w swoim App.xaml.cs lub kontrolerze IOC i ustawić ją na pierwszej stronie, która masz ładunek.
Nate Diamond

Odpowiedzi:

149

To jest preferowany sposób:

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

Zaletą tego jest to, że pobiera główny CoreApplicationViewi jest zawsze dostępny. Więcej szczegółów tutaj .

Istnieją dwie alternatywy, z których możesz skorzystać.

Pierwsza alternatywa

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

Spowoduje to pobranie aktywnego widoku aplikacji, ale da ci to wartość zerową , jeśli żadne widoki nie zostały aktywowane. Więcej szczegółów tutaj .

Druga alternatywa

Window.Current.Dispatcher

To rozwiązanie nie będzie działać, gdy jest wywoływane z innego wątku, ponieważ zwraca wartość null zamiast Dispatcher interfejsu użytkownika . Więcej szczegółów tutaj .

MAXE
źródło
Próbowałem tego, ale kiedy śledzę kod, kod delegata nadal jest wykonywany w wątku roboczym, a nie w „głównym wątku”.
Robert Oschler,
3
Należy pamiętać, że (przynajmniej w systemie Windows 8.1) DispatcherPriority to teraz CoreDispatcherPriority
Illidan
2
To działałoby tak długo, jak mamy jedną ASTA (apartament jednowątkowy aplikacji). W przypadku, gdy wprowadzimy funkcję „współdzielenia celu”, istnieje wiele ASTA (każdy z własnym dyspozytorem). A następnie CoreApplication.MainView może mieć wartość null (ponieważ jej ASTA nie została jeszcze zainicjowana). Być świadomym!
Yury Schkatula
Widziałem, że CoreApplication.MainView powoduje zawieszanie się programu po wywołaniu z wątku spoza interfejsu użytkownika. Musiałem ukryć CoreApplication.MainView.CoreWindow.Dispatcher podczas uruchamiania, aby uzyskać do niego dostęp później.
sjb-sjb
15

Dla każdego, kto używa C ++ / CX

Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
    CoreDispatcherPriority::Normal,
    ref new Windows::UI::Core::DispatchedHandler([this]()
{
    // do stuff
}));
Brett Pennings
źródło
1
„W przypadku tworzenia i korzystania z interfejsów API środowiska wykonawczego systemu Windows przy użyciu języka C ++ istnieje C ++ / WinRT. Jest to zalecany przez firmę Microsoft zamiennik biblioteki szablonów Windows Runtime C ++ (WRL) i C ++ / CX”. C ++ / WinRT
Richard Chambers
2
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
            CoreDispatcherPriority.Normal,
            () => { // your code should be here});
apramc
źródło
1

Chociaż jest to stary wątek, chciałem zwrócić uwagę na możliwy problem, z którym mogą się spotkać programiści, który wpłynął na mnie i bardzo utrudnił debugowanie w dużych aplikacjach UWP. W moim przypadku w 2014 roku przerobiłem poniższy kod z powyższych sugestii, ale od czasu do czasu nękany był przez sporadyczne zawieszanie się aplikacji, które miało charakter losowy.

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

Z powyższego korzystałem z klasy statycznej, aby umożliwić wywołanie Dispatchera w całej aplikacji - pozwalając na pojedyncze wywołanie. W 95% przypadków wszystko było w porządku, nawet przy regresji kontroli jakości, ale klienci od czasu do czasu zgłaszali problem. Rozwiązaniem było dołączenie wywołania poniżej, bez używania wywołania statycznego na rzeczywistych stronach.

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

            });

Nie dzieje się tak, gdy muszę upewnić się, że wątek interfejsu użytkownika został wywołany z App.xaml.cs lub mojej usługi Singleton NavigationService, która obsługiwała wypychanie / wyskakiwanie na stos. Dyspozytor najwyraźniej tracił informacje o tym, który wątek interfejsu użytkownika został wywołany, ponieważ każda strona ma swój własny wątek interfejsu użytkownika, gdy stos miał różne komunikaty wyzwalane z MessageBus.

Mam nadzieję, że pomoże to innym, na których może to mieć wpływ, i myślę, że jest to również miejsce, w którym każda platforma świadczyłaby usługę swoim programistom, publikując kompletny projekt obejmujący najlepsze praktyki.

Dave Friedel
źródło
0

Właściwie zaproponowałbym coś w tym stylu:

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

W ten sposób, gdybyś otworzył inny Widok / Okno, nie pomylisz dyspozytorów ...

Ten mały klejnot sprawdza, czy jest nawet okno. Jeśli nie ma, użyj Dispatcher MainView. Jeśli jest widok, użyj tego Dyspozytora.

JH
źródło