Cómo realizar cambios en la interfaz desde un hilo en background

A menudo existe la necesidad de actualizar la pantalla después de que la app realice una tarea en background. Un caso es en un flujo típico de autenticación, en el que es necesario redirigir al usuario a distintas pantallas dependiendo de el éxito o fracaso en el consumo de un webservice. Por ejemplo, si integras login con Facebook o Twitter en tu app, desde la pantalla de Login es necesario redirigir al usuario a una pantalla de “Login exitoso” o “Tus datos son incorrectos”.

El problema es que es muy mala práctica hacer cambios a la interfaz de usuario desde tu ViewModel o un thread en background. Digamos que si tienes un flujo de este estilo:

esquema

Conceptualmente, lo recomendado por MVVM es que desde tu ViewModel envíes un mensaje a la o las pantallas que tengan que reaccionar a ese mensaje. Por ejemplo, quizás quieras notificar al usuario de que su descarga en background terminó exitosamente u ocurrió un error, porcentajes de descarga, etcétera. En Windows Phone existe una librería muy practica que se llama MVVM Light, que permite hacer exactamente ésto.

Para ilustrar un uso práctico de ésto, en una de mis apps (TransantiagoMaster), al hacer una planificación de recorridos, es necesario que una de las páginas cambie de estado y muestre el resultado de la planificación. Este cambio de estado se realiza a nivel de la vista.

Primero añadimos en nuestro proyecto la dependencia a MVVM light en Package Manager Console:

install-package mvvmlight

levantando la consolainstalando el paquete

Luego sólo tenemos que hacer uso de la funcionalidad. Primero creamos una clase que represente al mensaje que queremos enviar. Acá podemos colocar todo tipo de atributos que representen parámetros, por ejemplo que al hacer login te retorne los atributos del usuario obtenidos desde el servidor, etcétera. Nuestro caso es muy sencillo y sólo usaremos esto:

namespace TransantiagoMaster.Messages
{
    public class PlanificacionDescargadaMessage
    {

    }
}

Luego en el ViewModel, en el manejador de eventos que descarga el resultado de la planificación desde el servidor, hacemos lo siguiente:

Messenger.Default.Send<PlanificacionDescargadaMessage>(new PlanificacionDescargadaMessage());

Messenger es una clase estática definida por el framework MVVM Light. Lo que estamos indicando en ese código es que estamos haciendo un broadcast o envío de un mensaje de tipo PlanificacionDescargadaMessage, y estamos creando una instancia de esa clase. Ahora lo que hay que hacer es declarar el receptor de ese mensaje. En este caso, en la página del mapa, en el constructor debe ir el siguiente código:

Messenger.Default.Register<PlanificacionDescargadaMessage>(this, (action) => ReceivePlanificacionMessage(action));

Y luego declarar el manejador de eventos de ese mensaje:

        private object ReceivePlanificacionMessage(PlanificacionDescargadaMessage action)
        {
            if (action != null)
            {
                Dispatcher.BeginInvoke(() =>
                {
                    // el código va acá
                    VisualStateManager.GoToState(this, "MostrarTipPlanificacion", true);
                });
            }

            return null;
        }

La vista durante la ejecución muestra un progressbar en la parte de arriba:

plan-pre

Al terminar de planificar, la vista cambia y se muestra parte del panel inferior:

plan-post

Importante destacar que, al cerrarse la vista, no habrá receptor para el mensaje y el mensaje se “pierde”. Si el mensaje es importante, se debe declarar la recepción en todas las vistas donde interese. Por ejemplo, quizás quieres tener un feedback visual mientras se descarga un archivo, en algunas pantallas con un progressbar, quizás en otras sólo con un toast.

 

Trackbacks & Pings

Agregar un comentario