Maneira correta de obter o CoreDispatcher em um aplicativo da Windows Store

83

Estou construindo um aplicativo da Windows Store e tenho alguns códigos que precisam ser postados no thread de interface do usuário.

Para isso, gostaria de recuperar o CoreDispatcher e usá-lo para postar o código.

Parece que existem algumas maneiras de fazer isso:

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

// Second way
Window.Current.Dispatcher;

Eu me pergunto qual é o correto? ou se ambos são equivalentes?

ácido lisérgico
fonte
3
Ambos estão meio corretos, mas será nulo se você não estiver acessando de algo que já tenha acesso ao Dispatcher. Se você quiser usá-lo em, digamos, um ViewModel ou Controller, você precisará armazenar fora do Dispatcher, geralmente como uma propriedade estática em seu App.xaml.cs ou controlador IOC, e configurá-lo na primeira página que você carregou.
Nate Diamond de

Respostas:

148

Esta é a forma preferida:

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

A vantagem disso é que ele obtém o principal CoreApplicationViewe está sempre disponível. Mais detalhes aqui .

Existem duas alternativas que você pode usar.

Primeira alternativa

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

Isso obtém a visualização ativa do aplicativo, mas resultará em nulo , se nenhuma visualização tiver sido ativada. Mais detalhes aqui .

Segunda alternativa

Window.Current.Dispatcher

Esta solução não funcionará quando for chamada de outro thread, pois retorna nulo em vez do UI Dispatcher . Mais detalhes aqui .

MAXE
fonte
Tentei fazer isso, mas quando rastreio o código, o código do delegado ainda está em execução em um thread de trabalho e não no "thread principal".
Robert Oschler
3
Observe que (pelo menos no Windows 8.1) DispatcherPriority agora é CoreDispatcherPriority
Illidan
2
Isso funcionaria contanto que tivéssemos ASTA único (aplicativo single-threaded apartment). No caso de introduzirmos o recurso de "compartilhamento de destino", existem vários ASTAs (cada um com seu próprio despachante). E então CoreApplication.MainView pode ser nulo (porque seu ASTA ainda não foi inicializado). Estar ciente!
Yury Schkatula
Eu vi CoreApplication.MainView fazer meu programa travar quando chamado de um thread não-UI. Tive que esconder o CoreApplication.MainView.CoreWindow.Dispatcher na inicialização para acessá-lo mais tarde.
sjb-sjb
15

Para qualquer pessoa que use C ++ / CX

Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
    CoreDispatcherPriority::Normal,
    ref new Windows::UI::Core::DispatchedHandler([this]()
{
    // do stuff
}));
Brett Pennings
fonte
1
"Para criar e consumir APIs do Windows Runtime usando C ++, existe o C ++ / WinRT. Esta é a substituição recomendada pela Microsoft para a Windows Runtime C ++ Template Library (WRL) e C ++ / CX." C ++ / WinRT
Richard Chambers
2
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
            CoreDispatcherPriority.Normal,
            () => { // your code should be here});
Apramc
fonte
1

Embora este seja um tópico antigo, eu queria chamar a atenção para um possível problema que os desenvolvedores podem encontrar e que me impactou e tornou extremamente difícil depurar em grandes aplicativos UWP. No meu caso, refatorei o código a seguir a partir das sugestões acima em 2014, mas ocasionalmente era atormentado por congelamentos ocasionais de aplicativos que eram de natureza aleatória.

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

Do exposto acima, usei uma classe estática para permitir a chamada do Dispatcher por todo o aplicativo - permitindo uma única chamada. Em 95% do tempo, tudo estava bem, mesmo com a regressão do controle de qualidade, mas os clientes relatavam um problema de vez em quando. A solução foi incluir a chamada abaixo, não usando uma chamada estática nas páginas reais.

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

            });

Este não é o caso quando eu preciso garantir que o UI Thread foi chamado de App.xaml.cs ou meu Singleton NavigationService que manipulou push / popping na pilha. O despachante aparentemente estava perdendo o controle de qual UI Thread foi chamado, uma vez que cada página tem seu próprio UI thread, quando a pilha tinha uma variedade de Mensagens disparadas do MessageBus.

Espero que isso ajude outras pessoas que possam ser afetadas e também é onde eu acho que cada plataforma prestaria um serviço aos seus desenvolvedores, publicando um projeto completo cobrindo as melhores práticas.

Dave Friedel
fonte
0

Na verdade, eu proporia algo nesta linha:

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

Dessa forma, se você abrir outra Visualização / Janela, não confundirá os Dispatchers ...

Esta pequena joia verifica se há uma janela. Se nenhum, use o Dispatcher do MainView. Se houver uma visualização, use o Dispatcher dessa.

JH
fonte