Windows Phone. Navegación. Paso de objetos.

Comenzamos recordando que en Windows Phone tenemos disponible el servicio de navegación llamado NavigationService que nos permite realizar operaciones como, navegar de una página a otra, navegar de una página a otra pasando parámetros, volver a una página en la que hemos estado, etc.

Ya vimos una introducción a la navegación entre páginas (la bases del sevicio de navegación, como pasar de una página a otra y como pasar parámetros simples como cadenas de una página a otra) en la siguiente entrada:

Windows Phone. Navegación entre páginas. 1º Parte.

Además, analizamos todos los eventos de navegación en esta otra entrada:

Windows Phone. Navegación entre páginas. 2º Parte.

Recordamos que en Windows Phone tenemos  dos eventos de navegación que nos permiten controlar la navegación entre páginas. Los dos métodos disponibles son:

  • OnNavigatedTo
  • OnNavigatedFrom

El primero de ellos se ejecutará siempre que una página pasa a ser la página activa (se entra en la página). El segundo de los eventos se ejecutará cuando la página deja de ser la página activa (salimos de la página).

El paso de parámetros visto hasta ahora (llamado Query String) nos permitía pasar parámetros simples (cadenas, números o valores boleanos). Podemos pasar los parámetros sencillamente tal y como lo haríamos en una página web (un signo de interrogación ? precede al primer parametro y los  subsiguientes van precedidos de un carácter &). Por ejemplo:

NavigationService.Navigate(new Uri("/Pagina.xaml?parametro=hola", UriKind.Relative));

En el ejemplo anterior, pasamos un parámetro llamado “parametro” con el valor “hola” a una página llamada “Pagina”.

Es un caso válido y simple. Lo podemos tener sin duda en el desarrollo de nuestras aplicaciones. Sin embargo, por desgracia no siempre será así. En ocasiones necesitaremos pasar datos mucho mas densos como objetos enteros con múltiples propiedades. Para ese tipo de situaciones el sistema de paso de parámetros visto hasta ahora se nos vuelve ineficiente.

¿Qué podemos hacer?

Vamos a utilizar para ello los eventos que ya hemos analizado, OnNavigatedTo y OnNavigatedFrom.

Comenzamos. Para ello, lo primero que vamos a necesitar para poder pasar un objeto de completo con todas sus propiedades de una página a otra es … la definición de ese objeto. En nuestro ejemplo vamos a crear una clase Telefono, con varias propiedades:

public class Telefono
{
     public Uri Imagen { get; set; }
     public string Marca { get; set; }
     public string Modelo { get; set; }
     public decimal Peso { get; set; }
}

Nuestro objetivo será pasar un objeto de tipo Telefono de una página 1 a una página 2. Nos centramos en la primera de las páginas. Necesitaremos algun elemento en la interfaz que provoque la navegación. Lo haremos de una manera muy simple utilizando un botón:

<Button x:Name="btnNavegar" Content="Ir a la Página 2" Height="100" Width="400" Background="OrangeRed" Click="btnNavegar_Click"/>

Visualmente quedaría asi:

private void btnNavegar_Click(object sender, RoutedEventArgs e)
{
     NavigationService.Navigate(new Uri("/Pagina2.xaml", UriKind.Relative));
}

Como se puede ver arriba, la navegación la haremos como hemos visto hasta ahora utilizando el servicio NavigationService.

Nota: Fíjate que NO pasamos ningún parámetro utilizando la Url.

Veamos la definición de la segunda página (a la que enviaremos al usuario tras pulsar el botón):

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
     <StackPanel>
          <Image x:Name="img"/>
          <TextBlock Text="Marca:"/>
          <TextBlock x:Name="tbMarca" FontSize="32"/>
          <TextBlock Text="Modelo:" Foreground="OrangeRed"/>
          <TextBlock x:Name="tbModelo" FontSize="32"/>
          <TextBlock Text="Peso:" Foreground="OrangeRed"/>
          <TextBlock x:Name="tbPeso" FontSize="32"/>
     </StackPanel>
</Grid>

Como ya hemos mencionado, para realizar el paso del objeto Telefono utilizaremos los eventos OnNavigatedTo y OnNavigatedFrom. Vamos a sobreescribir el evento OnNavigatedFrom de nuestra primera página:

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
     base.OnNavigatedFrom(e);
}

El evento OnNavigatedFrom se lanzará justo antes de cerrar nuestra primera página y antes de mostrar la segunda. Aquí es donde viene la parte interesante. En este método tenemos acceso a las propiedades del argumento NavigationEventArgs. Una de ellas, llamada Content, tiene como contenido la instancia de la página a la que vamos a navegar (página2). Por lo tanto, podemos hacer algo tán sencillo como lo siguiente:

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
     base.OnNavigatedFrom(e);

     Pagina2 pagina2 = e.Content as Pagina2;

     if (pagina2 != null)
     {
          Telefono telefono = new Telefono();
          telefono.Imagen = new Uri("/IMGS/Lumia800.jpg", UriKind.Relative);
          telefono.Marca = "Nokia";
          telefono.Modelo = "Lumia 800";
          telefono.Peso = 142;

          pagina2.DataContext = telefono;

          /*
          //Otra posibilidad sin hacer uso de binding:
          BitmapImage bi = new BitmapImage();
          bi.UriSource = telefono.Imagen;

          pagina2.img.Source = bi;
          pagina2.tbMarca.Text = telefono.Marca;
          pagina2.tbModelo.Text = telefono.Modelo;
          pagina2.tbPeso.Text = string.Format("{0} gr", telefono.Peso);
          * */
     }
}

¿Que hemos hecho?

Hemos hecho un casting de la propiedad Content a nuestra segunda página. De esta forma podemos tener acceso a ella. Hemos creado un objeto de tipo Telefono con todas sus propiedades para asignarlo como el DataContext de la segunda página.

Nota: Tenéis también un trozo de código comentado en caso de no utilizar DataBinding.

Veamos como modificar la interfaz de la segunda página para añadir los Bindings necesarios:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
     <StackPanel>
          <Image x:Name="img" Source="{Binding Imagen}"/>
          <TextBlock Text="Marca:" Foreground="OrangeRed"/>
          <TextBlock x:Name="tbMarca" Text="{Binding Marca}" FontSize="32"/>
          <TextBlock Text="Modelo:" Foreground="OrangeRed"/>
          <TextBlock x:Name="tbModelo" Text="{Binding Modelo}" FontSize="32"/>
          <TextBlock Text="Peso:" Foreground="OrangeRed"/>
          <TextBlock x:Name="tbPeso" Text="{Binding Peso}" FontSize="32"/>
     </StackPanel>
</Grid>

El resultado visual de la segunda página tras recibir el parámetro sería:

Puedes ver en video el resultado de nuestro ejemplo a continuación:

Puedes descargar el ejemplo realizado:

Con esta entrada avanzamos un poco más en todas las posibilidades que tenemos en la navegación entre páginas. Nos faltan aún puntos interesantes como:

  • Navegación entre páginas utilizando MVVM.
  • Animación para el paso entre páginas.

Ambos puntos los veremos el próximas entradas asi que permaneced atentos.

Si os surgen dudas o sugerencias recordar que podéis dejarlas en los comentarios.

Más Información:

Eugenedotnet: Passing values between Windows Phone 7 pages: Destination page instance within OnNavigatedFrom method.

Enzo Contini Blog: How to pass Data between Pages in Windows Phone 7.

19 pensamientos en “Windows Phone. Navegación. Paso de objetos.

  1. Buenas Javier

    Lo primero felicidades por este tutorial, está todo muy bien explicado, con ejemplos y muy entendible.

    Lo segundo sería preguntarte si sabes si existe alguna forma de hacer referencia a un objeto que esta en una pagina 1, desde una página 2.

    Por ejemplo, yo tengo en mi pagina1 un textblock y desde mi pagina2 quiero modificar la propiedad Text de ese textblock a “Hola” (por ejemplo). En mi caso en particular lo que quiero es modificar desde esa pagina2 la propiedad Source de un elemento Mediaelemente que esta en una pagina1.

    Felicidades por el blog y espero tu respuesta ^^

    • Buenas!

      Muchas gracias. Espero continuar con artículos cada vez más interesantes y completos. La pregunta que formulas es sumamente interesante. La próxima entrada la tengo ya parcialmente escrita, pero daría como para una entrada. Lo anoto para posibles futuras entradas.
      Bien, entrando en situación, el MediaElement permite reproducir archivos de audio y video. Imagino que tu objetivo será establecer la propiedad Source indicando la Uri al medio a reproducir. Opciones para realizar la operación son varias (también depende de como estes estructurando el código, si usas MVVM o no, etc):

      -La primera de las opciones hace uso de la propiedad Content del NavigationEventArgs (como en la entrada). En la segunda página utilizas el evento OnNavigatedFrom:

      protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
      {
      base.OnNavigatedFrom(e);

      MainPage page = e.Content as MainPage;

      page.ApplicationTitle.Text = “Test”;
      }

      Por ejemplo, de esta forma hemos accedido al título de la primera página desde la segunda, modificandolo.

      – Otra opción. Podemos utilizar el servicio PhoneApplicationService. Guardamos en la segunda página la URL y la rescatamos al volver de la página:

      Guardar:
      PhoneApplicationService.Current.State[“url”] = “https://javiersuarezruiz.wordpress.com”;

      Recuperar:
      var url = PhoneApplicationService.Current.State[“url”];

      Hay más opciones (usar IsolatedStorage, BBDD, comunicación entre ViewModels, etc).
      Espero que cualquiera de las opciones vistas te sea válida. Me parece un tema intereante. Intentaré en una futura entrada analizar formas de resolver este problema, con pros y contras.
      Cualquier otra duda, ya sabes que puedes preguntar sin problemas.

      Un saludo.

  2. Hola nuevamente amigo sabes estoy probando la navegación, pero de un tuto que esta acá http://www.cristalab.com/tutoriales/programacion-para-windows-phone-7-interfaz-y-diseno-c94787l/ hasta ahora me va bien con dos pestañas como noticias y artículos pero a la hora de querer agregar otra no consigo que pueda navegar el código que se debe modificar es

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
    base.OnNavigatedTo(e);
    int index = int.Parse(this.NavigationContext.QueryString[“id”]);
    string collectionName = this.NavigationContext.QueryString[“collName”];

    if (string.CompareOrdinal(collectionName, “Noticias”) == 0)
    {
    if (App.ViewModel != null && App.ViewModel.Noticias != null && App.ViewModel.Noticias.Count > 0)
    {

    this.DataContext =
    App.ViewModel.Articulos[index];
    }
    }
    else
    {
    if (App.ViewModel != null && App.ViewModel.Articulos != null && App.ViewModel.Articulos.Count > 0)
    {

    this.DataContext =
    App.ViewModel.Articulos[index];
    }

    }
    }

    Ubicado en RssDetailPage.xaml.cs, alli se ve claramente cuales son las dos ventanas que enlaza como es el caso de Artículos y Noticias pero a la hora de querer agregar otra no se como podría ser y el otro fragmento de código ubicado en MainPage.xaml.cs es:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
    Control c = sender as Control;
    if (c != null)
    {
    ElementoEntradaVideo elemento = c.DataContext as ElementoEntradaVideo;
    if (elemento != null)
    {
    NavigationService.Navigate(new Uri(
    string.Format(“/DetalleVideo.xaml?id={0}”,
    App.ViewModel.Videos.IndexOf(elemento)), UriKind.Relative));
    }
    else
    {
    ElementoEntradaRss element = c.DataContext as ElementoEntradaRss;

    if (element != null)
    {
    ObservableCollection coll;
    string collName;

    if (App.ViewModel.Noticias.Contains(element))
    {
    coll = App.ViewModel.Noticias;
    collName = “Noticias”;
    }
    else
    {
    coll = App.ViewModel.Articulos;
    collName = “Articulos”;
    }

    NavigationService.Navigate(new Uri(
    string.Format(“/RssDetailPage.xaml?id={0}&collName={1}”,
    coll.IndexOf(element), collName), UriKind.Relative));
    }
    }
    }
    }

    Me podrías ayudar allí tengo ya casi todo el código montado pero al intentar modifica que si agregando otro else o if el ensamblador no me deja ejecutar.

    Saludos y disculpa las molestias.

      • A esto conseguí una manera de agregar otra aunque no es la mejor opción ya que el código no queda tan limpio. Si puedes ayudarme te lo agradezco por cierto antier mande la aplicación (V1.0) a la tienda cuando este disponible me gustaría que la probaras y medieras sugerencias ya que estoy comenzando a programar acá en WP, yo se es PHP en un nivel considerable. Sabes de parrilla tv “robe” por decirlo así el color de fondo para mi aplicación ya que no es ni blanco ni negro esta intermedio. Saludos

      • Hola Cristian!

        Me alegro que hayas encontrado por ti mismo una solución al problema. Si quieres, por correo mejor (por el simple motivo de poder adjuntar ejemplos) analizamos otras posibles formas de realizar lo mismo.

        El periodo de certificación viene siendo de entre 3 a 5 días. Asi que tu aplicación si todo va bien debería estar a punto. Por supuesto que estaré encantado de poder probarla y darte mi opinión.

        Ah, en cuanto al fondo. No hay problemas. Si te ha servido de inspiración, por mi bien.

        Un saludo.

  3. Hola Javier. Estoy introduciéndome en WP8 y estoy siguiendo varios de tus tutoriales cada vez que busco algo en internet jeje
    Justamente ahora estoy tratando de pasar las propiedades de una Imagen aplicada en una Pagina,a otra… pero por lo visto hay algo que me falta porque no se muestran en la segunda pagina.

    PAGINA1 – Llama el evento para abrir la segunda pagina
    private void btnCrear_Click(object sender, EventArgs e)
    {
    NavigationService.Navigate(new Uri(“/CrearPanorama.xaml” , UriKind.Relative));

    }

    PAGINA2 – La segunda Pagina donde se abriría la pagina con las imágenes cargadas en la pagina anterior.

    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
    {
    base.OnNavigatedFrom(e);

    CrearPanoramix crearImagen = e.Content as CrearPanoramix;

    if (crearImagen != null)
    {

    Img1.Source = crearImagen.myImageL.Source;
    Img2.Source = crearImagen.myImage2.Source;
    Img3.Source = crearImagen.myImage3.Source;
    Img4.Source = crearImagen.myImage4.Source;
    Img5.Source = crearImagen.myImageR.Source;
    //MessageBox.Show(Img1.Source);

    }
    }

    Necesitaría tu ayuda para ver porque no se visualizan las imágenes, el código no me da error por eso no se por donde atacar el problema. Muchas Gracias!🙂

  4. Ya lo solucione… no había entendido que tanto cuando llamas el evento navigate y el metodo OnNavigateFrom son en la primera pagina.
    Saludos, muy buen blog

  5. hola que tal, tengo una gran duda y hasta este momento no me he dado la idea de como resolverla, me gustaria en la aplicacion, osease, que en un texbox uno inserte una palabra y que al darle click en un respectivo boton de go, te mande a la pagina a la cual se le destino ese nombre (pagina en la aplicacion, pantalla) si pudieras ayudarme con esa duda te lo agradeceroa

  6. Creo que ya te lo había preguntado, tengo un ListBox y quiero pasar los datos que están dentro de el a otra pagina, como podría hacerlo?? espero que me puedas ayudar, gracias

    • Hola!

      Me imagino que quieres pasar el elemento seleccionado de la lista y pasarlo a la siguiente página, ¿no?
      Hay varios factores que determinan como hacerlo:
      – Si usas WP8 o WP8.1
      – Si usas MVVM o no

      Si no usas MVVM, podrías usar el evento SelectionChanged del control listado para obtener el elemento seleccionado.
      Si usas MVVM, podrías bindear la propiedad SelectedItem a un objeto de tu viewmodel y capturarlo en el set.
      Luego, para realizar el envio, dependiendo de si usas WP8 o WP8.1:
      – WP8: https://javiersuarezruiz.wordpress.com/2012/06/07/windows-phone-navegacion-paso-de-objetos/
      – WP8.1: Frame.Navigate(typeof(Page), parameter);

      Añade más información y te intento ayudar con mayor exactitud😉

      Un saludo.

      • Estoy usando VS 2010 para Windows Phone, y encontre que para seleccion el valor del ListBox es asi:

        string nombre = ((TextBlock)((StackPanel)((Button)e.OriginalSource).Parent).FindName(“txtNombre”)).Text;

        Creo que esta bien, pero ahora tengo otro problema, son 3 datos los que tengo que pasar a una pagina y cuando uso NavigationService para pasarlos, solo puedo pasar el primero, he intentado pasar los 3 pero no se como, espero me tu sepas como hacerlo, y gracias por tu tiempo

  7. Muchas gracias por tus aportes, son realmente útiles ya que no se encuentra tan buena información en la web como en los libros.

  8. Necesito pasar datos de varios textboxs seria posible pasarlos todos con la propiedad navigation service en una misma linea ya que esta no me funciona

  9. En mi codigo tendo esto NavigationService.Navigate(new Uri(“/MainPageUser.xaml”, UriKind.RelativeOrAbsolute));
    Navigate me da error indicando que ‘object’ no contiende una definicion para ‘navigate’ ni se encuentra ningun metodo de extencion ‘navigate’ ( ¿falta alguna directiva using o una referencia de ensamblado ?)

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net.Http;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;

    // La plantilla de elemento Página en blanco está documentada en http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

    namespace APPUniversal10
    {
    ///
    /// Página vacía que se puede usar de forma independiente o a la que se puede navegar dentro de un objeto Frame.
    ///
    public sealed partial class MainPage : Page
    {
    public MainPage()
    {
    this.InitializeComponent();
    }

    public object NavigationService { get; private set; }

    private async void BtnIniciar_Click(object sender, RoutedEventArgs e)
    {
    var app_windowsmobile_usuario_login = txtUsername.Text; // Devuelve el texto
    var app_windowsmobile_clave_login = psbPassword.Password; // Devuelve el texto

    using (var client = new HttpClient())
    {
    var values = new Dictionary
    {
    { “windows_usuario_login”, txtUsername.Text },
    { “window_clave_login”, psbPassword.Password },
    { “windows_token”, “1991” }
    };

    var content = new FormUrlEncodedContent(values);

    var response = await client.PostAsync(“http://mipagina.com.ve/app-window-/login.php”, content);

    var responseString = await response.Content.ReadAsStringAsync();

    // MessageDialog dialog = new MessageDialog(responseString);
    // await dialog.ShowAsync();

    NavigationService.Navigate(new Uri(“/MainPageUser.xaml”, UriKind.RelativeOrAbsolute)); // en esta linea Navigate da error

    // Frame.Navigate(typeof(MainPageUser)); he leido en foros que esta es la manera pero tambien me da error
    // Excepción producida: ‘System.AccessViolationException’ en APPUniversal10.exe
    }
    }
    }

    }

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