[Universal Apps] Navegación entre páginas, paso de parámetros y gestión de la tecla volver (2/2)

transitionsIntroducción

Anteriormente vimos como como se realiza la navegación entre páginas en Windows Phone 8.1 además de aprender como gestionar la tecla física atrás. En el artículo actual pasaremos a aplicar y utilizar los conceptos básicos previos tal y como lo utilizaríamos en una aplicación real. Vamos a aprender a navegar utilizando un servicio de navegación e incluso

MVVM en escena!

Realmente podemos mantener una implementación MVVM muy similar a como hacíamos hasta ahora en Windows Phone 8. Comenzamos por la base. Contamos con dos ficheros clave en nuestro proyecto Universal para implementar el patrón MVVM:

  • PageBase
  • ViewModelBase

PageBase

Como revelamos con su nombre, será una clase de la que heredaran todas las páginas de nuestra aplicación. Y si, comentamos que serán todas las páginas de la aplicación (ya sean del proyecto Windows o del proyecto Windows Phone). Las páginas en ambas plataformas es exactamente igual, un objeto de tipo Page. Esta clase cuenta con varios objetivos:

  • Establecer el Frame activo en todo momento para que el acceso al mismo sea sencillo.
  • Permitir el acceso de los eventos de navegación desde nuestras viewmodels.

Además podría interesarnos además:

  • Gestionar las transiciones entre páginas.
  • Gestionar estados (Loading, etc.)
  • Gestionar el estado segun la conexión de red.

Con la funcionalidad básica (facilitarnos el acceso a Frame y eventos de navegación) quedaría:

public class PageBase : Page
{
     private ViewModelBase _vm;

     protected override void OnNavigatedTo(NavigationEventArgs e)
     {
         base.OnNavigatedTo(e);

         _vm = (ViewModelBase)this.DataContext;
         _vm.SetAppFrame(this.Frame);
         _vm.OnNavigatedTo(e);
     }

     protected override void OnNavigatedFrom(NavigationEventArgs e)
     {
         base.OnNavigatedFrom(e);
         _vm.OnNavigatedFrom(e);
     }
}

Gestionamos los eventos básicos de navegación (al entrar y al salir de la página) de modo que, en el método OnNavigateTo obtenemos la viewmodel de la página y asignamos el Frame.

ViewModelBase

Continuamos con la segunda de nuestras clases base. En esta ocasión trataremos la clase base de la que heredarán todos los viewmodels. Los objetivos básicos de la clase son:

  • Notificar cambios (implementar INotifyPropertyChanged).
  • Acceso al objeto Frame que nos permitirá realizar la navegación.
  • Permitir el acceso a los eventos de navegación.
public abstract class ViewModelBase : INotifyPropertyChanged
{
     private Frame appFrame;
     private bool isBusy;

     public Frame AppFrame
     {
         get { return appFrame; }
     }

     public bool IsBusy
     {
         get { return isBusy; }
         set
         {
             isBusy = value;
             RaisePropertyChanged();
         }
     }

     public event PropertyChangedEventHandler PropertyChanged;

     public abstract Task OnNavigatedFrom(NavigationEventArgs args);

     public abstract Task OnNavigatedTo(NavigationEventArgs args);

     public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
     {
         var Handler = PropertyChanged;
         if (Handler != null)
             Handler(this, new PropertyChangedEventArgs(propertyName));
     }

     internal void SetAppFrame(Frame viewFrame)
     {
         appFrame = viewFrame;
     }
}

El acceso a los eventos de navegación lo lograremos implementando en la viewmodel los métodos abstractos OnNavigatedFrom y OnNavigatedTo. Esto no permite guardar y recuperar parámetros o estados.

Utilizando PageBase

En la carpeta Views añadiremos nuestras páginas. En nuestro ejemplo, tendremos dos páginas:

  • Pagina1
  • Pagina2

Cada página será un objeto de tipo PageBase, tanto en XAML:

<base:PageBase
    x:Class="Ejemplo_NavegacionMVVM02.Views.Pagina1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:base="using:Ejemplo_NavegacionMVVM02.Views.Base"
    mc:Ignorable="d"
    Background="LightCoral">
    <Grid>

    </Grid>
</base:PageBase>

Como en el code-behind:

/// <summary>
/// Pagina1.
/// </summary>
public sealed partial class Pagina1 : PageBase
{
     public Pagina1()
     {
         this.InitializeComponent();
     }
}

Añadimos en la primera página (Pagina1) un botón que permita navegar a la segunda página:

<Button Content="Navegar a página 2"
        HorizontalAlignment="Center"
        />
El botón que nos permitirá navegar

El botón que nos permitirá navegar

Para permitir navegar a la segunda página, necesitamos definir un comando en la viewmodel. Creamos una clase derivada de ViewModelBase:

public class Pagina1ViewModel : ViewModelBase
{

}

Implementamos los métodos de navegación definidos en ViewModelBase:

public class Pagina1ViewModel : ViewModelBase
{
     public override System.Threading.Tasks.Task OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
     {
         return null;
     }

     public override System.Threading.Tasks.Task OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
     {
         return null;
     }
}

Y añadimos un comando que permita navegar de la página principal a la segunda página:

public class Pagina1ViewModel : ViewModelBase
{
     //Commands
     private ICommand _navigateCommand;

     public override System.Threading.Tasks.Task OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
     {
         return null;
     }

     public override System.Threading.Tasks.Task OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
     {
         return null;
     }

     public ICommand NavigateCommand
     {
         get { return _navigateCommand = _navigateCommand ?? new DelegateCommand(NavigateCommandDelegate); }
     }

     public void NavigateCommandDelegate()
     {
         this.AppFrame.Navigate(typeof(Pagina2), "Esto es un parámetro");
     }
}

Recordamos que el control Frame hospeda controles Page y tiene un historial de navegación que se puede utilizar para ir hacia atrás y hacia adelante por las páginas que ya visitaste. Tras obtener el Frame correspondiente, utilizamos el método Navigate para realizar la navegación a otra página. Tenemos dos sobrescrituras del método Navigate:

  • Navigate(TypeName). Provoca que el Frame cargue el contenido especificado por el tipo pasado como parámetro.
  • Navigate(TypeName, Object). En este caso, además de indicar el tipo del contenido a cargar (primer parámetro), podemos pasar un parámetro a la página que se navega(segundo parámetro).

En nuestro ejemplo hemos utilizado la segunda sobrescritura del método pasando un parámetro.

Ahora ,necesitamos conectar nuestra vista con nuestro viewmodel. Podemos hacerlo de múltiples formas, desde el constructor de la vista, instanciandola en App, usando Ioc. En nuestro caso, utilizaremos Ioc. Usaremos Unity.

Añadimos Unity

Añadimos Unity

Una vez añadida la referencia correspondiente en cada proyecto (Windows y Windows Phone) creamos una nueva clase en la carpeta Base dentro de la carpeta ViewModels llamada ViewModelLocator:

public class ViewModelLocator
{
     readonly IUnityContainer _container;

     public ViewModelLocator()
     {
         _container = new UnityContainer();

         _container.RegisterType<Pagina1ViewModel>();
         _container.RegisterType<Pagina2ViewModel>();
     }

     public Pagina1ViewModel Pagina1ViewModel
     {
         get { return _container.Resolve<Pagina1ViewModel>(); }
     }

     public Pagina2ViewModel Pagina2ViewModel
     {
         get { return _container.Resolve<Pagina2ViewModel>(); }
     }
}

Sencillamente registramos nuestras viewmodels y creamos un par de propiedades públicas por cada viewmodel para poder resolverlas y acceder a ellas desde las vistas. A continuación, registramos nuestro locator en App.xaml que también tenemos en el proyecto Shared:

<locator:ViewModelLocator x:Key="Locator"/>

Asignamos la viewmodel como DataContext de nuestra viewmodel:

DataContext="{Binding Pagina1ViewModel, Source={StaticResource Locator}}"

Y todo preparado!. Nuestra segunda página sera similar a la primera (tipo PageBase):

<base:PageBase
    x:Class="Ejemplo_NavegacionMVVM02.Views.Pagina2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:base="using:Ejemplo_NavegacionMVVM02.Views.Base"
    mc:Ignorable="d"
    Background="LightSeaGreen"
    DataContext="{Binding Pagina2ViewModel, Source={StaticResource Locator}}">
    <Grid>
        <Button Content="Volver"
                HorizontalAlignment="Center"
                Command="{Binding NavigateBackCommand}"/>
    </Grid>
</base:PageBase>
Volver atrás

Volver atrás

Contará con su propia viewmodel:

public class Pagina2ViewModel : ViewModelBase
{
     //Commands
     private ICommand _navigateBackCommand;

     public override System.Threading.Tasks.Task OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
     {
         return null;
     }

     public override System.Threading.Tasks.Task OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
     {
         if (args.Parameter != null)
             Debug.WriteLine(args.Parameter);

         return null;
     }

     public ICommand NavigateBackCommand
     {
         get { return _navigateBackCommand = _navigateBackCommand ?? new DelegateCommand(NavigateBackCommandDelegate); }
     }

     public void NavigateBackCommandDelegate()
     {
            this.AppFrame.GoBack();
     }
}

Utilizamos el método GoBack del Frame que navega al elemento más inmediato del historial de navegación, la página anterior.

Podéis descargar el ejemplo realizado a continuación:

Recordar qur cualquier duda o comentario lo podéis dejar en los comentarios.

Más información

22 pensamientos en “[Universal Apps] Navegación entre páginas, paso de parámetros y gestión de la tecla volver (2/2)

  1. Pingback: [Windows Phone 8.1] El Action Center | Javier Suárez | Blog

  2. Pingback: [Universal App] Uso del Zoom Semantico, creando Jumplists | Javier Suárez | Blog

  3. Pingback: [Universal App] Uso del Zoom Semantico, creando Jumplists - Javier Suárez

  4. Pingback: [Windows Phone 8.1] La nueva StatusBar | Javier Suárez | Blog

  5. Pingback: [Windows Phone 8.1] La nueva StatusBar - Javier Suárez

  6. Pingback: [Windows Phone 8.1] Integrando nuestra aplicación con Cortana | Javier Suárez | Blog

  7. Pingback: [Windows Phone 8.1] Probando notificaciones en el emulador | Javier Suárez | Blog

  8. Pingback: [Windows Phone 8.1] Probando notificaciones en el emulador - Javier Suárez

  9. Pingback: [Windows Phone 8.1] Crear menu lateral deslizante (similar al de la App Facebook) | Javier Suárez | Blog

  10. Pingback: [Windows Phone 8.1] Crear menu lateral deslizante (similar al de la App Facebook) - Javier Suárez

  11. Pingback: [Universal App] Usando Microsoft OCR | Javier Suárez | Blog

  12. Pingback: [Windows Phone 8.1] Capturando la pantalla utilizando la API MediaCapture | Javier Suárez | Blog

  13. Pingback: [Universal App] Usando las APIs Credential Locker | Javier Suárez | Blog

  14. Pingback: [Universal App] Usando las APIs Credential Locker - Javier Suárez

  15. Pingback: [Universal App] Roaming Settings, compartiendo datos entre plataformas | Javier Suárez | Blog

  16. Pingback: [Windows Phone 8.1] Reproducir Audio en Background | Javier Suárez | Blog

  17. Pingback: [Windows Phone 8.1] Usando el sensor de luz | Javier Suárez | Blog

  18. Pingback: [Universal Apps/ Xamarin.Forms] XCC. Compilación condicional en XAML | Javier Suárez | Blog

  19. Pingback: [Universal Apps/ Xamarin.Forms] XCC. Compilación condicional en XAML - Javier Suárez

  20. Pingback: [Tips and Tricks] Universal Apps. Behavior para gestionar gestos swipe con comando | Javier Suárez | Blog

  21. Pingback: [Tips and Tricks] Universal Apps. Behavior para gestionar gestos swipe con comando - Javier Suárez

Deja un comentario