Navegación jerárquica

Download SampleDescargar el ejemplo

La clase NavigationPage proporciona una experiencia de navegación jerárquica en la que el usuario puede navegar por las páginas hacia adelante y hacia atrás, como quiera. La clase implementa la navegación como una pila de objetos de página en la que el último en entrar es el primero en salir (LIFO). En este artículo se muestra cómo utilizar la clase NavigationPage para realizar la navegación en una pila de páginas.

Para pasar de una página a otra, una aplicación insertará una página nueva en la pila de navegación, donde se convertirá en la página activa, tal como se muestra en el diagrama siguiente.

Pushing a Page to the Navigation Stack

Para volver a la página anterior, la aplicación mostrará la página actual de la pila de navegación y la nueva página de nivel superior se convertirá en la página activa, tal como se muestra en el diagrama siguiente:

Popping a Page from the Navigation Stack

La propiedad Navigation expone los métodos de navegación en cualquiera de los tipos derivados de Page. Estos métodos proporcionan la capacidad para insertar páginas en la pila de navegación, sacar páginas de la pila de navegación y manipular la pila.

Realizar la navegación

En la navegación jerárquica, se usa la clase NavigationPage para navegar por una pila de objetos ContentPage. En las siguientes capturas de pantalla, se muestran los componentes principales de la NavigationPage en cada plataforma:

NavigationPage Components

El diseño de una NavigationPage depende de la plataforma:

  • En iOS, en la parte superior de la página, hay una barra de navegación que muestra un título y presenta un botón Atrás que vuelve a la página anterior.
  • En Android, en la parte superior de la página, hay una barra de navegación que muestra un título, un icono y un botón Atrás que vuelve a la página anterior. El icono se define en el atributo [Activity] que decora la clase MainActivity en el proyecto específico de la plataforma Android.
  • En la Plataforma universal de Windows, en la parte superior de la página, hay una barra de navegación que muestra un título.

En todas las plataformas, el valor de la propiedad Page.Title se mostrará como el título de la página. Además, la propiedad IconColor se puede establecer en un elemento Color que se aplica al icono de la barra de navegación.

Nota:

Se recomienda que una NavigationPage debe rellenarse únicamente con instancias de ContentPage.

Creación de la página raíz

La primera página que se agrega a una pila de navegación se denomina página raíz de la aplicación. En el ejemplo de código siguiente se muestra cómo se consigue:

public App ()
{
  MainPage = new NavigationPage (new Page1Xaml ());
}

Esto hace que la instancia de Page1XamlContentPage se inserte en la pila de navegación, donde se convertirá en la página activa y en la página raíz de la aplicación. Esto se muestra en las capturas de pantalla siguientes:

Root Page of Navigation Stack

Nota:

La propiedad RootPage de una instancia de NavigationPage permite acceder a la primera página de la pila de navegación.

Inserción de páginas en la pila de navegación

Para navegar a Page2Xaml, es necesario invocar el método PushAsync en la propiedad Navigation de la página actual, como se muestra en el ejemplo de código siguiente:

async void OnNextPageButtonClicked (object sender, EventArgs e)
{
  await Navigation.PushAsync (new Page2Xaml ());
}

Esto hace que la instancia de Page2Xaml se inserte en la pila de navegación, donde se convertirá en la página activa. Esto se muestra en las capturas de pantalla siguientes:

Page Pushed onto Navigation Stack

Al invocar el método PushAsync, ocurre lo siguiente:

  • Se invoca la invalidación de OnDisappearing de la página que llama a PushAsync.
  • Se invoca la invalidación de OnAppearing de la página a la que se ha navegado.
  • La tarea PushAsync finaliza.

Sin embargo, el orden exacto en el que se producen estos eventos depende de la plataforma. Para obtener más información, consulte el capítulo 24 del libro sobre Xamarin.Forms de Charles Petzold.

Nota:

Las llamadas a OnDisappearing y las invalidaciones de OnAppearing no se pueden tratar como indicaciones garantizadas de navegación de páginas. Por ejemplo, en iOS, la invalidación de OnDisappearing se llama en la página activa cuando la aplicación finaliza.

Sacar páginas de la pila de navegación

La página activa se puede extraer de la pila de navegación. Para ello, pulse el botón Atrás del dispositivo, independientemente de si se trata de un botón físico en el dispositivo o de un botón en la pantalla.

Para volver mediante programación a la página original, la instancia Page2Xaml debe invocar el método PopAsync, como se muestra en el ejemplo de código siguiente:

async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
  await Navigation.PopAsync ();
}

Esto hace que la instancia de Page2Xaml se quite de la pila de navegación y que la nueva página de nivel superior se convierta en la página activa. Al invocar el método PopAsync, ocurre lo siguiente:

  • Se invoca la invalidación de OnDisappearing de la página que llama a PopAsync.
  • Se invoca la invalidación de OnAppearing de la página a la que se ha regresado.
  • La tarea PopAsync vuelve.

Sin embargo, el orden exacto en el que se producen estos eventos depende de la plataforma. Para obtener más información, consulte el capítulo 24 del libro sobre Xamarin.Forms de Charles Petzold.

Al igual que los métodos PushAsync y PopAsync, la propiedad Navigation de cada página también proporciona un método PopToRootAsync, que se muestra en el ejemplo de código siguiente:

async void OnRootPageButtonClicked (object sender, EventArgs e)
{
  await Navigation.PopToRootAsync ();
}

Este método saca todas las páginas de la pila de navegación, excepto la Page raíz, de manera que la página raíz de la aplicación se convierte en la página activa.

Animación de transiciones de página

La propiedad Navigation de cada página también proporciona métodos de inserción y extracción invalidados que incluyen un parámetro boolean que controla si se debe mostrar una animación de página durante la navegación, como se muestra en el ejemplo de código siguiente:

async void OnNextPageButtonClicked (object sender, EventArgs e)
{
  // Page appearance not animated
  await Navigation.PushAsync (new Page2Xaml (), false);
}

async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
  // Page appearance not animated
  await Navigation.PopAsync (false);
}

async void OnRootPageButtonClicked (object sender, EventArgs e)
{
  // Page appearance not animated
  await Navigation.PopToRootAsync (false);
}

Al establecer el parámetro boolean en false, la animación de transición de página se deshabilita, mientras que al establecer el parámetro en true, la animación de transición de página se habilita, siempre que la plataforma subyacente lo admita. Sin embargo, los métodos de inserción y extracción que carecen de este parámetro habilitan la animación de manera predeterminada.

Pasar datos al navegar

A veces es necesario que una página pase datos a otra durante la navegación. Dos técnicas para llevar a cabo esto son pasar datos a través de un constructor de página y establecer el BindingContext de la página nueva en los datos. A continuación, se explicarán de uno en uno.

Pasar datos a través de un constructor de página

La técnica más sencilla para pasar datos a otra página durante la navegación es hacerlo a través de un parámetro de constructor de página, que se muestra en el siguiente ejemplo de código:

public App ()
{
  MainPage = new NavigationPage (new MainPage (DateTime.Now.ToString ("u")));
}

Este código crea una instancia de MainPage y pasa la fecha y la hora actuales en formato ISO8601, que se encapsula en una instancia de NavigationPage.

La instancia de MainPage recibe los datos a través de un parámetro de constructor, tal como se muestra en el ejemplo de código siguiente:

public MainPage (string date)
{
  InitializeComponent ();
  dateLabel.Text = date;
}

A continuación, se establece la propiedad Label.Text para que los datos se muestren en la página, como se muestra en las capturas de pantalla siguientes:

Data Passed Through a Page Constructor

Pasar datos a través de un objeto BindingContext

Un enfoque alternativo para pasar datos a otra página durante la navegación es establecer el BindingContext de la página nueva en los datos, como se muestra en el siguiente ejemplo de código:

async void OnNavigateButtonClicked (object sender, EventArgs e)
{
  var contact = new Contact {
    Name = "Jane Doe",
    Age = 30,
    Occupation = "Developer",
    Country = "USA"
  };

  var secondPage = new SecondPage ();
  secondPage.BindingContext = contact;
  await Navigation.PushAsync (secondPage);
}

Este código establece el BindingContext de la instancia de SecondPage en la instancia de Contact y después navega a la SecondPage.

Después, la SecondPage utiliza el enlace de datos para mostrar los datos de la instancia de Contact, como se muestra en el ejemplo de código XAML siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PassingData.SecondPage"
             Title="Second Page">
    <ContentPage.Content>
        <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
            <StackLayout Orientation="Horizontal">
                <Label Text="Name:" HorizontalOptions="FillAndExpand" />
                <Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
            </StackLayout>
            ...
            <Button x:Name="navigateButton" Text="Previous Page" Clicked="OnNavigateButtonClicked" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

En el ejemplo de código siguiente se muestra cómo se puede realizar el enlace de datos en C#:

public class SecondPageCS : ContentPage
{
  public SecondPageCS ()
  {
    var nameLabel = new Label {
      FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
      FontAttributes = FontAttributes.Bold
    };
    nameLabel.SetBinding (Label.TextProperty, "Name");
    ...
    var navigateButton = new Button { Text = "Previous Page" };
    navigateButton.Clicked += OnNavigateButtonClicked;

    Content = new StackLayout {
      HorizontalOptions = LayoutOptions.Center,
      VerticalOptions = LayoutOptions.Center,
      Children = {
        new StackLayout {
          Orientation = StackOrientation.Horizontal,
          Children = {
            new Label{ Text = "Name:", FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)), HorizontalOptions = LayoutOptions.FillAndExpand },
            nameLabel
          }
        },
        ...
        navigateButton
      }
    };
  }

  async void OnNavigateButtonClicked (object sender, EventArgs e)
  {
    await Navigation.PopAsync ();
  }
}

A continuación, una serie de controles Label muestran los datos en la página, como se muestra en las capturas de pantalla siguientes:

Data Passed Through a BindingContext

Para más información sobre el enlace de datos, consulte Data Binding Basics (Aspectos básicos del enlace de datos).

Manipulación de la pila de navegación

La propiedad Navigation expone una propiedad NavigationStack desde la que se pueden obtener las páginas de la pila de navegación. Mientras que Xamarin.Forms mantiene el acceso a la pila de navegación, la propiedad Navigation proporciona los métodos InsertPageBefore y RemovePage para manipular la pila mediante la inserción o la eliminación de páginas.

El método InsertPageBefore inserta una página especificada en la pila de navegación antes de una página existente especificada, como se muestra en el diagrama siguiente:

Inserting a Page in the Navigation Stack

El método RemovePage quita la página especificada de la pila de navegación, como se muestra en el diagrama siguiente:

Removing a Page from the Navigation Stack

Estos métodos permiten una experiencia de navegación personalizada, como sustituir una página de inicio de sesión por una página nueva, después de iniciar sesión correctamente. El ejemplo de código siguiente muestra esta situación:

async void OnLoginButtonClicked (object sender, EventArgs e)
{
  ...
  var isValid = AreCredentialsCorrect (user);
  if (isValid) {
    App.IsUserLoggedIn = true;
    Navigation.InsertPageBefore (new MainPage (), this);
    await Navigation.PopAsync ();
  } else {
    // Login failed
  }
}

Siempre que las credenciales del usuario sean correctas, la instancia de MainPage se inserta en la pila de navegación antes de la página actual. A continuación, el método PopAsync quita la página actual de la pila de navegación y la instancia de MainPage se convierte en la página activa.

Mostrar vistas en la barra de navegación

Cualquier View de Xamarin.Forms se puede mostrar en la barra de navegación de una NavigationPage. Para ello, establezca la propiedad adjunta NavigationPage.TitleView en una View. Esta propiedad adjunta se puede establecer en cualquier Page y, cuando la Page se inserte en una NavigationPage, la NavigationPage respetará el valor de la propiedad.

En el ejemplo siguiente, tomado del Ejemplo de vista de título, se muestra cómo establecer la propiedad adjunta NavigationPage.TitleView de XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="NavigationPageTitleView.TitleViewPage">
    <NavigationPage.TitleView>
        <Slider HeightRequest="44" WidthRequest="300" />
    </NavigationPage.TitleView>
    ...
</ContentPage>

El código equivalente en C# es el siguiente:

public class TitleViewPage : ContentPage
{
    public TitleViewPage()
    {
        var titleView = new Slider { HeightRequest = 44, WidthRequest = 300 };
        NavigationPage.SetTitleView(this, titleView);
        ...
    }
}

Esto da como resultado un Slider que se muestra en la barra de navegación en la NavigationPage:

Slider TitleView

Importante

Muchas vistas no aparecen en la barra de navegación, salvo que el tamaño de la vista se especifique con las propiedades WidthRequest y HeightRequest. Como alternativa, la vista puede encapsularse en un StackLayout con las propiedades HorizontalOptions y VerticalOptions establecidas en los valores adecuados.

Tenga en cuenta que, dado que la clase Layout deriva de la clase View, la propiedad adjunta TitleView se puede establecer para mostrar una clase de diseño que contiene varias vistas. En iOS y la Plataforma universal de Windows (UWP), la altura de la barra de navegación no se puede cambiar y, por tanto, se recortará si la vista que se muestra en la barra de navegación es mayor que el tamaño predeterminado de la barra de navegación. Sin embargo, en Android, la altura de la barra de navegación se puede cambiar estableciendo la propiedad enlazable NavigationPage.BarHeight en un double que represente la altura nueva. Para obtener más información, consulte Establecer la altura de la barra de navegación en una NavigationPage.

Como alternativa, para sugerir una barra de exploración extendida, se puede colocar una parte del contenido en la barra de navegación y otra en una vista en la parte superior del contenido de la página que coincida con el color de la barra de navegación. Además, en iOS, la línea y la sombra del separador que se encuentran en la parte inferior de la barra de navegación se pueden quitar estableciendo la propiedad enlazable NavigationPage.HideNavigationBarSeparator en true. Para obtener más información, consulte Ocultar el separador de la barra de navegación en una NavigationPage.

Nota:

Las propiedades BackButtonTitle, Title, TitleIcon y TitleView pueden definir valores que ocupan espacio en la barra de navegación. Mientras que el tamaño de la barra de navegación varía según la plataforma y el tamaño de pantalla, al establecer todas estas propiedades se producirán conflictos a causa del espacio limitado disponible. En lugar de intentar utilizar una combinación de estas propiedades, es posible que, si solo establece la propiedad TitleView, pueda elaborar mejor el diseño de la barra de navegación deseado.

Limitaciones

Al mostrar una View en la barra de navegación de una NavigationPage, hay una serie de limitaciones que debe conocer:

  • En iOS, las vistas colocadas en la barra de navegación de una NavigationPage se muestran en una posición diferente según si están habilitados los títulos de gran tamaño. Para obtener más información sobre cómo habilitar títulos grandes, consulte Mostrar títulos grandes.
  • En Android, colocar las vistas en la barra de navegación de una NavigationPage solo puede realizarse en las aplicaciones que utilizan la compatibilidad de aplicaciones.
  • No se recomienda colocar vistas grandes y complejas, como ListView y TableView, en la barra de navegación de una NavigationPage.