Forma correcta de obtener CoreDispatcher en una aplicación de la Tienda Windows

83

Estoy creando una aplicación de la Tienda Windows y tengo un código que debe publicarse en el hilo de la interfaz de usuario.

Para eso, me gustaría recuperar el CoreDispatcher y usarlo para publicar el código.

Parece que hay algunas formas de hacerlo:

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

// Second way
Window.Current.Dispatcher;

Me pregunto cuál es la correcta. o si ambos son equivalentes?

ácido lisérgico
fuente
3
Ambos son la clase de correcta, pero será nula si no está accediendo a ella a partir de algo que ya tiene acceso al despachador. Si desea usarlo en, digamos, un ViewModel o Controller, entonces deberá almacenar el Dispatcher, generalmente como una propiedad estática en su App.xaml.cs o controlador IOC, y configurarlo desde la primera página que tienes carga.
Nate Diamond

Respuestas:

148

Esta es la forma preferida:

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

La ventaja que tiene esto es que obtiene el principal CoreApplicationViewy, por tanto, siempre está disponible. Más detalles aquí .

Hay dos alternativas que puede utilizar.

Primera alternativa

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

Esto obtiene la vista activa para la aplicación, pero esto le dará nulo , si no se ha activado ninguna vista. Más detalles aquí .

Segunda alternativa

Window.Current.Dispatcher

Esta solución no funcionará cuando se llame desde otro hilo, ya que devuelve un valor nulo en lugar de UI Dispatcher . Más detalles aquí .

MAXE
fuente
Intenté esto, pero cuando rastreo el código, el código delegado todavía se está ejecutando en un hilo de trabajo y no en el "hilo principal".
Robert Oschler
3
Tenga en cuenta que (al menos en Windows 8.1) DispatcherPriority ahora es CoreDispatcherPriority
Illidan
2
Esto funcionaría siempre que tengamos un solo ASTA (apartamento de aplicación de un solo subproceso). En caso de que introduzcamos la función "compartir objetivo", hay varios ASTA (cada uno con su propio despachador). Y luego CoreApplication.MainView puede ser nulo (porque su ASTA aún no está inicializado). ¡Ten cuidado!
Yury Schkatula
He visto que CoreApplication.MainView hace que mi programa se cuelgue cuando se llama desde un hilo que no es de UI. Tuve que esconder CoreApplication.MainView.CoreWindow.Dispatcher al inicio para poder acceder a él más tarde.
sjb-sjb
15

Para cualquiera 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
fuente
1
"Para crear y consumir las API de Windows Runtime con C ++, existe C ++ / WinRT. Este es el reemplazo recomendado por Microsoft para la biblioteca de plantillas de Windows Runtime C ++ (WRL) y C ++ / CX". C ++ / WinRT
Richard Chambers
2
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
            CoreDispatcherPriority.Normal,
            () => { // your code should be here});
apramc
fuente
1

Si bien este es un hilo antiguo, quería llamar la atención sobre un posible problema que los desarrolladores pueden encontrar, lo que me impactó y dificultó enormemente la depuración en aplicaciones de UWP grandes. En mi caso, refactoricé el siguiente código de las sugerencias anteriores en 2014, pero ocasionalmente estaba plagado de congelamientos ocasionales de aplicaciones que eran de naturaleza aleatoria.

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

De lo anterior, había usado una clase estática para permitir la llamada del Dispatcher en toda la aplicación, lo que permite una sola llamada. Durante el 95% del tiempo, todo estuvo bien incluso a través de la regresión de control de calidad, pero los clientes informaban un problema de vez en cuando. La solución fue incluir la llamada a continuación, no usar una llamada estática en las páginas reales.

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

            });

Este no es el caso cuando necesito asegurarme de que se llamó al subproceso de la interfaz de usuario desde App.xaml.cs o mi Singleton NavigationService que manejó empujar / hacer estallar en la pila. El despachador aparentemente estaba perdiendo la pista de qué subproceso de la interfaz de usuario se llamó, ya que cada página tiene su propio subproceso de la interfaz de usuario, cuando la pila tenía una variedad de mensajes que se activaban desde MessageBus.

Espero que esto ayude a otros que puedan verse afectados y también es donde creo que cada plataforma haría un servicio a sus desarrolladores al publicar un proyecto completo que cubra las mejores prácticas.

Dave Friedel
fuente
0

En realidad, propondría algo en la línea de esto:

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

De esa manera, si ha abierto otra Vista / Ventana, no confundirá a los Despachadores ...

Esta pequeña joya comprueba si incluso hay una ventana. Si no hay ninguno, use el Dispatcher de MainView. Si hay una vista, use el Dispatcher de esa.

J H
fuente