Compartilhar via


Navegação hierárquica

A classe NavigationPage oferece uma experiência de navegação hierárquica em que o usuário pode navegar pelas páginas para frente e para trás, conforme desejado. A classe implementa a navegação como uma pilha UEPS (último a entrar, primeiro a sair) de objetos de Página. Este artigo demonstra como usar a classe NavigationPage para executar a navegação em uma pilha de páginas.

Para ir de uma página para outra, um aplicativo enviará por push uma nova página para a pilha de navegação, na qual ela se tornará a página ativa, conforme mostrado no diagrama a seguir:

Enviando uma página por push para a pilha de navegação

Para retornar à página anterior, o aplicativo removerá a página atual da pilha de navegação e, em seguida, a nova página de nível superior se tornará a página ativa, conforme mostrado no diagrama a seguir:

Removendo uma página da pilha de navegação

Os métodos de navegação são expostos pela propriedade Navigation em qualquer tipo derivado de Page. Esses métodos possibilitam enviar páginas por push para a pilha de navegação, remover páginas da pilha de navegação e executar a manipulação da pilha.

Executando a navegação

Na barra de navegação hierárquica, a classe NavigationPage é usada para navegar por meio de uma pilha de objetos ContentPage. As capturas de tela a seguir mostram os principais componentes de NavigationPage em cada plataforma:

Componentes de NavigationPage

O layout de um NavigationPage depende da plataforma:

  • No iOS, há uma barra de navegação na parte superior da página que exibe um título e que tem um botão Voltar que leva à página anterior.
  • No Android, há uma barra de navegação na parte superior da página que exibe um título, um ícone e um botão Voltar que leva à página anterior. O ícone é definido no atributo [Activity] que decora a classe MainActivity no projeto específico da plataforma Android.
  • Na Plataforma Universal do Windows, há uma barra de navegação na parte superior da página que exibe um título.

Em todas as plataformas, o valor da propriedade Page.Title será exibido como o título da página. Além disso, a IconColor propriedade pode ser definida como um Color que é aplicado ao ícone na barra de navegação.

Observação

É recomendável que um NavigationPage seja preenchido somente com instâncias de ContentPage.

Criando a Página Raiz

A primeira página adicionada a uma pilha de navegação é conhecida como a página raiz do aplicativo e o exemplo de código a seguir mostra como isso é realizado:

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

Isso faz com que a instância ContentPage de Page1Xaml seja enviada por push para a pilha de navegação, em que se torna a página ativa e a página raiz do aplicativo. Isso é mostrado nas seguintes capturas de tela:

Página Raiz da pilha de navegação

Observação

A propriedade RootPage de uma instância de NavigationPage fornece acesso à primeira página na pilha de navegação.

Enviando páginas por push para a pilha de navegação

Para navegar até Page2Xaml, é necessário invocar o método PushAsync na propriedade Navigation da página atual, conforme demonstrado no exemplo de código a seguir:

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

Isso faz com que a instância Page2Xaml seja enviada por push para a pilha de navegação, em que ele se torna a página ativa. Isso é mostrado nas seguintes capturas de tela:

Página enviada por push para a pilha de navegação

Quando o método PushAsync é invocado, os seguintes eventos ocorrem:

  • A página que chama PushAsync tem sua substituição OnDisappearing invocada.
  • A página para a qual o usuário está navegando tem sua substituição OnAppearing invocada.
  • A tarefa PushAsync é concluída.

No entanto, a ordem exata em que esses eventos ocorrem depende da plataforma. Para mais informações, ver Capítulo 24 do livro de Xamarin.Forms Charles Petzold.

Observação

Chamadas para as substituições OnDisappearing e OnAppearing não podem ser tratadas como indicações garantidas de navegação de página. Por exemplo, no iOS, a substituição OnDisappearing é chamada na página ativa quando o aplicativo é encerrado.

Removendo páginas da pilha de navegação

A página ativa pode ser removida como o item mais recente da pilha de navegação pressionando o botão Voltar no dispositivo, independentemente de este ser um botão físico no dispositivo ou um botão na tela.

Para retornar programaticamente à página original, a instância Page2Xaml deve invocar o método PopAsync, conforme demonstrado no exemplo de código a seguir:

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

Isso faz com que a instância Page2Xaml seja removida da pilha de navegação, com a nova página de nível superior tornando-se a página ativa. Quando o método PopAsync é invocado, os seguintes eventos ocorrem:

  • A página que chama PopAsync tem sua substituição OnDisappearing invocada.
  • A página para a qual o usuário está voltando tem sua substituição OnAppearing invocada.
  • A tarefa PopAsync é retornada.

No entanto, a ordem exata em que esses eventos ocorrem depende da plataforma. Para mais informações, ver o capítulo 24 do livro de Xamarin.Forms Charles Petzold.

Assim como os métodos PushAsync e PopAsync, a propriedade Navigation de cada página também fornece um método PopToRootAsync, que é mostrado no exemplo de código a seguir:

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

Esse método retira todas a páginas, exceto pela Page raiz, da pilha de navegação, tornando assim a página raiz do aplicativo a página ativa.

Animando transições de página

A propriedade Navigation de cada página também fornece métodos de envio por push e remoção substituídos que incluem um parâmetro boolean que controla se deve ser exibida uma animação de página durante a navegação, conforme mostrado no código de exemplo a seguir:

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);
}

Definir o parâmetro boolean como false desabilita a animação de transição de página, enquanto definir o parâmetro como true habilita a animação de transição de página, desde que ela tenha suporte da plataforma subjacente. No entanto, os métodos de envio por push e remoção que não têm esse parâmetro habilitam a animação por padrão.

Transmitindo dados ao navegar

Às vezes, é necessário que uma página transmita dados para outra página durante a navegação. Duas técnicas para fazer isso são transmitir dados por meio de um construtor de página e definir o BindingContext como os dados. Descreveremos cada técnica separadamente.

Transmitindo dados por meio de um construtor de página

A técnica mais simples para transmitir dados para outra página durante a navegação é por meio de um parâmetro de construtor de página, que é mostrado no exemplo de código a seguir:

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

Esse código cria uma instância de MainPage, transmitindo a data e a hora atuais no formato ISO8601, que é encapsulada em uma instância de NavigationPage.

A instância de MainPage recebe os dados por meio de um parâmetro de construtor, conforme mostrado no exemplo de código a seguir:

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

Os dados são exibidos na página definindo a propriedade Label.Text, conforme mostrado nas capturas de tela seguir:

Dados transmitidos por meio de um construtor de página

Transmitindo dados por meio de um BindingContext

Outra abordagem para transmitir dados para outra página durante a navegação é definir o BindingContext da nova página como os dados, conforme mostrado no exemplo de código a seguir:

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);
}

Esse código define o BindingContext da instância de SecondPage como a instância de Contact e, em seguida, navega para o SecondPage.

O SecondPage, em seguida, usa a associação de dados para exibir os dados da instância de Contact, conforme mostrado no exemplo de código XAML a seguir:

<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>

O exemplo de código a seguir mostra como a associação de dados pode ser feita em 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 ();
  }
}

Em seguida, os dados são exibidos na página por uma série de controles de Label, conforme mostrado nas capturas de tela seguir:

Dados transmitidos por meio de um BindingContext

Para obter mais informações sobre vinculação de dados, veja Noções básicas de vinculação de dados.

Manipulando a pilha de navegação

A propriedade Navigation expõe uma propriedade NavigationStack da qual as páginas na pilha de navegação podem ser obtidas. Enquanto Xamarin.Forms mantém o acesso à pilha de navegação, a Navigation propriedade fornece os InsertPageBefore métodos e RemovePage para manipular a pilha inserindo páginas ou removendo-as.

O método InsertPageBefore insere uma página especificada na pilha de navegação antes de uma página existente, conforme mostrado no diagrama a seguir:

Inserindo uma página na pilha de navegação

O método RemovePage remove a página especificada da pilha de navegação, conforme mostrado no diagrama a seguir:

Removendo uma página da pilha de navegação

Esses métodos permitem criar uma experiência de navegação personalizada, como substituir uma página de logon por uma nova página após um logon bem-sucedido. O código de exemplo a seguir demonstra esse cenário:

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
  }
}

Desde que as credenciais do usuário estejam corretas, a instância MainPage será inserida na pilha de navegação antes da página atual. Em seguida, o método PopAsync remove a página atual da pilha de navegação, com a instância de MainPage se tornando a página ativa.

Exibindo modos de exibição na barra de navegação

Qualquer um Xamarin.FormsView pode ser exibido na barra de navegação de um NavigationPagearquivo . Isso é feito definindo a propriedade anexada NavigationPage.TitleView como um View. Essa propriedade anexada pode ser definida em qualquer Page e, quando a Page é enviada por push a uma NavigationPage, a NavigationPage respeitará o valor da propriedade.

O exemplo a seguir mostra como definir a propriedade anexada 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>

Este é o código C# equivalente:

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

Isso faz com que um Slider seja exibido na barra de navegação na NavigationPage:

Slider TitleView

Importante

Muitos modos de exibição não aparecerão na barra de navegação a menos que o tamanho do modo de exibição seja especificado com as propriedades WidthRequest e HeightRequest. Como alternativa, o modo de exibição pode ser encapsulado em um StackLayout com as propriedades HorizontalOptions e VerticalOptions definidas como os valores apropriados.

Observe que, como a classe Layout é derivada da classe View, a propriedade anexada TitleView pode ser definida para exibir uma classe de layout que contém vários modos de exibição. No iOS e na UWP (Plataforma Universal do Windows), não é possível alterar a altura da barra de navegação e, portanto, será feito um recorte se o modo de exibição na barra de navegação for maior que o tamanho padrão da barra de navegação. No entanto, no Android, a altura da barra de navegação pode ser alterada definindo a propriedade vinculável NavigationPage.BarHeight como um double que representa a nova altura. Para obter mais informações, confira Definindo a altura da barra de navegação em uma NavigationPage.

Como alternativa, é possível sugerir uma barra de navegação estendida colocando parte do conteúdo na barra de navegação e parte em um modo de exibição na parte superior do conteúdo da página, cuja cor deve corresponder à da barra de navegação. Além disso, no iOS, a linha separadora e sombra na parte inferior da barra de navegação podem ser removidas definindo a propriedade associável NavigationPage.HideNavigationBarSeparator como true. Para obter mais informações, confira Ocultando o separador da barra de navegação em uma NavigationPage.

Observação

As propriedades BackButtonTitle, Title, TitleIcon e TitleView podem definir valores que ocupam espaço na barra de navegação. Embora o tamanho da barra de navegação varie de acordo com o tamanho da tela e a plataforma, definir todas essas propriedades causará conflitos devido à limitação do espaço disponível. Em vez de tentar usar uma combinação dessas propriedades, você provavelmente concluirá que é mais fácil obter o design desejado da barra de navegação definindo apenas a propriedade TitleView.

Limitações

É necessário estar ciente de uma série de limitações ao exibir um View na barra de navegação de uma NavigationPage:

  • No iOS, modos de exibição colocados na barra de navegação de um NavigationPage poderão aparecer em uma posição diferente se títulos grandes estiverem habilitados. Para obter mais informações sobre a habilitação de títulos grandes, confira Exibindo títulos grandes.
  • No Android, só é possível colocar modos de exibição na barra de navegação de um NavigationPage em aplicativos que usam a compatibilidade de aplicativos.
  • Não é recomendável colocar modos de exibição grandes e complexos, como ListView e TableView, na barra de navegação de uma NavigationPage.