Empezando con Windows Phone 7.1 (y XV) – Navegación con MVVM Light


En Windows Phone 7, la navegación se maneja desde la vista. Concretamente, todas las páginas de una aplicación comparten la misma instancia del servicio de navegación a través de una propiedad: Page.NavigationService

Los patrones de diseño para la interfaz de usuario como MVP, MVC o MVVM procuran sacar de la Vista toda la lógica de navegación posible. Nos encontramos, sin embargo, que en Windows Phone, el servicio de navegación se accede desde la propia PhoneApplicationPage. Concretamente, en una aplicación WP7 el responsable de la navegación es el marco principal (main frame) o RootFrame.

Existen varias alternativas para implementar navegación utilizando el patrón MVVM y, concretamente, haciendo uso de MVVM Light.

Navegación con Mensajes

Una primera forma es haciendo uso de la infraestructura de mensajes que proporciona MVVM Light. Esta infraestructura es utilizada con frecuencia en el patrón MVP para coordinar diferentes vistas. Me dejo una entrada en el futuro para los detalles de este tipo de infraestructuras. Si queréis saber cómo implementar navegación haciendo uso de esta técnica, echadle un vistazo a este artículo.

Utilizando el Servicio de Navegación desde la Vista Modelo

Para esta entrada he preferido sin embargo, otra aproximación que es hacer uso del servicio de navegación desde la Vista-Modelo.

Si le echáis un vistazo al servicio de navegación que tenemos en Windows Phone, os encontraréis con esto.

image

De toda la firma me voy a quedar con lo justo declarando una interfaz muy sencilla.

 1: /// <summary>

 2: /// Interfaz del servicio de navegación.

 3: /// </summary>

 4: public interface INavigationService

 5: {

 6:     /// <summary>

 7:     /// Evento de navegación

 8:     /// </summary>

 9:     event NavigatingCancelEventHandler Navigating;

 10:

 11:     /// <summary>

 12:     /// Permite navegar a la página indicada como parámetro

 13:     /// </summary>

 14:     /// <param name="pageUri">Instancia de <see cref="Uri"/> que hace referencia a la página a la que se navega</param>

 15:     void NavigateTo(Uri pageUri);

 16:

 17:     /// <summary>

 18:     /// Permite navegar hacia atrás en la pila de navegación

 19:     /// </summary>

 20:     void GoBack();

 21: }

Ahora, crearemos una clase para el servicio de navegación que implemente esta interfaz (acordaos de lo que os decía más arriba sobre el hecho de que es el RootFrame de una página el que se encarga de acceder al servicio).

En mi caso, estas clases se están yendo a una biblioteca de utilidades con lo que tendré que añadir una referencia al Assembly Microsoft.Phone.

 1: namespace Jdmveira.WindowsPhone.Mvvm.Navigation

 2: {

 3:     using System;

 4:     using System.Windows;

 5:     using Microsoft.Phone.Controls;

 6:

 7:     /// <summary>

 8:     /// Clase encargada de implementar el servicio de navegación

 9:     /// </summary>

 10:     public class NavigationService : INavigationService

 11:     {

 12:         #region Constructores

 13:

 14:         /// <summary>

 15:         /// Constructor por defecto de la clase

 16:         /// </summary>

 17:         public NavigationService()

 18:         {

 19:             // No podemos estar seguros de que en cualquier momento existe el RootFrame.

 20:             EnsureMainFrame();

 21:         }

 22:

 23:         #endregion Constructores

 24:

 25:         #region Miembros Públicos

 26:

 27:         /// <summary>

 28:         /// Evento de navegación

 29:         /// </summary>

 30:         public event System.Windows.Navigation.NavigatingCancelEventHandler Navigating;

 31:

 32:         /// <summary>

 33:         /// Método para navegar a la página indicada por la <see cref="Uri"/> pasada como parámetro

 34:         /// </summary>

 35:         /// <param name="pageUri">Instancia de <see cref="Uri"/> que indica la página a la que se navega</param>

 36:         public void NavigateTo(Uri pageUri)

 37:         {

 38:             if (EnsureMainFrame())

 39:             {

 40:                 _mainFrame.Navigate(pageUri);

 41:             }

 42:         }

 43:

 44:         /// <summary>

 45:         /// Navega hacia atrás en la pila de navegación

 46:         /// </summary>

 47:         public void GoBack()

 48:         {

 49:             if (EnsureMainFrame())

 50:             {

 51:                 _mainFrame.GoBack();

 52:             }

 53:         }

 54:

 55:         #endregion Miembros Públicos

 56:

 57:         #region Miembros Privados

 58:

 59:         /// <summary>

 60:         /// Se asegura de que existe una referncia al frame principal de una página

 61:         /// </summary>

 62:         private bool EnsureMainFrame()

 63:         {

 64:             if (_mainFrame != null)

 65:                 return true;

 66:

 67:             _mainFrame = Application.Current.RootVisual as PhoneApplicationFrame;

 68:             if (_mainFrame != null)

 69:             {

 70:                 // Adjuntamos nuestro propio manejador de eventos

 71:                 _mainFrame.Navigating += (s, e) =>

 72:                     {

 73:                         if (Navigating != null)

 74:                             Navigating(s, e);

 75:                     };

 76:

 77:                 return true;

 78:             }

 79:

 80:             return false;

 81:         }

 82:

 83:         private PhoneApplicationFrame _mainFrame;

 84:

 85:         #endregion Miembros Privados

 86:     }

 87: }

A partir de aquí, registrar el servicio en nuestro IoC y utilizarlo desde cualquier Vista-Modelo es un asunto trivial.

Echadle un vistazo a este post, de donde he sacado esta solución.

Pegas a esta implementación

Fundamentalmente, que en el fondo, la implementación depende de la clase PhoneApplicationFrame, pero no podemos evitar esto. ¿Verdad? Guiño

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s