Xamarin.Forms Navegação do shell

Baixar exemplo Baixar o exemplo

Xamarin.Forms O Shell inclui uma experiência de navegação baseada em URI que usa rotas para navegar até qualquer página no aplicativo, sem precisar seguir uma hierarquia de navegação definida. Além disso, eles também oferecem a capacidade de navegar para trás, sem precisar visitar todas as páginas na pilha de navegação.

A Shell classe define as seguintes propriedades relacionadas à navegação:

As propriedades BackButtonBehavior, CurrentItem e CurrentState são apoiadas por objetos BindableProperty, o que significa que elas podem ser destino de vinculações de dados.

A navegação é executada pela invocação do método GoToAsync, da classe Shell. Quando a navegação está prestes a ser executada, o Navigating evento é acionado e o evento é acionado quando a Navigated navegação é concluída.

Observação

A navegação ainda pode ser executada entre páginas em um aplicativo Shell usando a propriedade Navigation . Saiba mais em Navegação hierárquica.

Rotas

A navegação é executada em um aplicativo Shell, especificando um URI para onde navegar. Os URIs de navegação podem ter três componentes:

  • Uma rota, que define o caminho para o conteúdo que existe como parte da hierarquia visual do Shell.
  • Uma página. As páginas que não existam na hierarquia visual do Shell podem ser enviadas por push para a pilha de navegação de qualquer lugar em um aplicativo Shell. Por exemplo, uma página de detalhes não será definida na hierarquia visual do Shell, mas poderá ser enviada por push para a pilha de navegação conforme necessário.
  • Um ou mais parâmetros de consulta. Parâmetros de consulta são aqueles que podem ser passados para a página de destino durante a navegação.

Quando um URI de navegação incluir todos os três componentes, a estrutura será: //route/page?queryParameters

Registrar rotas

As rotas podem ser definidas em FlyoutItemobjetos , TabBarTab, e ShellContent , por meio de suas Route propriedades:

<Shell ...>
    <FlyoutItem ...
                Route="animals">
        <Tab ...
             Route="domestic">
            <ShellContent ...
                          Route="cats" />
            <ShellContent ...
                          Route="dogs" />
        </Tab>
        <ShellContent ...
                      Route="monkeys" />
        <ShellContent ...
                      Route="elephants" />  
        <ShellContent ...
                      Route="bears" />
    </FlyoutItem>
    <ShellContent ...
                  Route="about" />                  
    ...
</Shell>

Observação

Todos os itens na hierarquia do Shell têm uma rota associada a eles. Se você não definir uma rota, uma será gerada em runtime. No entanto, não há garantia de que as rotas geradas sejam consistentes em sessões de aplicativos diferentes.

O exemplo acima cria a seguinte hierarquia de rotas, que pode ser usada na navegação programática:

animals
  domestic
    cats
    dogs
  monkeys
  elephants
  bears
about

Para navegar até o objeto ShellContent para a rota dogs, o URI da rota absoluta é //animals/domestic/dogs. Da mesma forma, para navegar até o objeto ShellContent para a rota about, o URI da rota absoluta é //about.

Aviso

Uma ArgumentException será lançada na inicialização do aplicativo se uma rota duplicada for detectada. Essa exceção também será gerada se duas ou mais rotas do mesmo nível na hierarquia compartilharem um nome.

Registrar rotas de página de detalhes

No construtor de subclasse Shell ou em qualquer outro local executado antes que uma rota seja invocada, rotas adicionais podem ser registradas explicitamente para qualquer página de detalhes que não seja representada na hierarquia visual do Shell. Isso é feito com o método Routing.RegisterRoute:

Routing.RegisterRoute("monkeydetails", typeof(MonkeyDetailPage));
Routing.RegisterRoute("beardetails", typeof(BearDetailPage));
Routing.RegisterRoute("catdetails", typeof(CatDetailPage));
Routing.RegisterRoute("dogdetails", typeof(DogDetailPage));
Routing.RegisterRoute("elephantdetails", typeof(ElephantDetailPage));

Este exemplo registra páginas de detalhes, que não são definidas na Shell subclasse, como rotas. Essas páginas de detalhes podem então ser navegadas para usando a navegação baseada em URI, de qualquer lugar dentro do aplicativo. As rotas para essas páginas são conhecidas como rotas globais.

Aviso

Um ArgumentException será lançado se o método Routing.RegisterRoute tentar registrar a mesma rota em dois ou mais tipos diferentes.

Como alternativa, as páginas podem ser registradas em hierarquias de rota diferentes, se necessário:

Routing.RegisterRoute("monkeys/details", typeof(MonkeyDetailPage));
Routing.RegisterRoute("bears/details", typeof(BearDetailPage));
Routing.RegisterRoute("cats/details", typeof(CatDetailPage));
Routing.RegisterRoute("dogs/details", typeof(DogDetailPage));
Routing.RegisterRoute("elephants/details", typeof(ElephantDetailPage));

Este exemplo habilita a navegação de páginas contextuais, onde a navegação até a rota details partindo da página para a rota monkeys exibe o MonkeyDetailPage. Da mesma forma, a navegação até a rota details partindo da página para a rota elephants exibe o ElephantDetailPage. Para obter mais informações, consulte Navegação contextual.

Observação

As páginas cujas rotas tenham sido registradas com o método Routing.RegisterRoute podem ter o registro cancelado com o método Routing.UnRegisterRoute, se necessário.

Realizar navegação

Para realizar a navegação, uma referência para a subclasse Shell deve ser obtida primeiro. Isso pode ser feito por meio da conversão da propriedade App.Current.MainPage em um objeto Shell, ou usando a propriedade Shell.Current. A navegação pode então ser realizada chamando o método GoToAsync no objeto Shell. Esse método navega até uma ShellNavigationState e retorna um Task, que será concluído depois que a animação de navegação for concluída. O objeto ShellNavigationState é construído pelo método GoToAsync, de uma string ou uma Uri, e ele tem a propriedade Location definida como o argumento string ou Uri.

Importante

Quando uma rota da hierarquia visual do Shell é navegada, uma pilha de navegação não é criada. Mas isso acontece quando uma página que não está na hierarquia visual do Shell é navegada.

O estado de navegação atual do Shell objeto pode ser recuperado por meio da Shell.Current.CurrentState propriedade , que inclui o URI da rota exibida na Location propriedade .

Rotas absolutas

A navegação pode ser realizada por meio da especificação de um URI absoluto válido como um argumento para o método GoToAsync:

await Shell.Current.GoToAsync("//animals/monkeys");

Este exemplo navega até a página para a rota monkeys, com a rota sendo definida em um objeto ShellContent. O objeto ShellContent que representa a rota monkeys é um filho de um objeto FlyoutItem, cuja rota é animals.

Rotas relativas

A navegação também pode ser realizada por meio da especificação de um URI relativo válido como um argumento para o método GoToAsync. O sistema de roteamento tentará fazer a correspondência do URI a um objeto ShellContent. Portanto, se todas as rotas em um aplicativo forem exclusivas, a navegação poderá ser executada especificando apenas o nome da rota exclusiva como um URI relativo.

Há suporte para os seguintes formatos de rota relativa:

Formatar Descrição
route A hierarquia de rotas será pesquisada para a rota especificada, para cima da posição atual. A página correspondente será enviada por push para a pilha de navegação.
/Rota A hierarquia de rotas será pesquisada a partir da rota especificada, para baixo da posição atual. A página correspondente será enviada por push para a pilha de navegação.
//Rota A hierarquia de rotas será pesquisada para a rota especificada, para cima da posição atual. A página correspondente substituirá a pilha de navegação.
///Rota A hierarquia de rotas será pesquisada para a rota especificada, para baixo da posição atual. A página correspondente substituirá a pilha de navegação.

O exemplo a seguir navega até a página da monkeydetails rota:

await Shell.Current.GoToAsync("monkeydetails");

Neste exemplo, a monkeyDetails rota é pesquisada para cima na hierarquia até que a página correspondente seja encontrada. Quando a página é encontrada, ela é enviada por push para a pilha de navegação.

Navegação contextual

As rotas relativas habilitam a navegação contextual. Por exemplo, considere a seguinte hierarquia de rotas:

monkeys
  details
bears
  details

Quando a página registrada para a rota monkeys for exibida, a navegação até a rota details exibirá a página registrada para a rota monkeys/details. Da mesma forma, quando a página registrada para a rota bears for exibida, a navegação até a rota details exibirá a página registrada para a rota bears/details. Saiba mais sobre como registrar as rotas neste exemplo em Registrar rotas de página.

Navegação regressiva

A navegação regressiva pode ser executada especificando ".." como o argumento para o método GoToAsync:

await Shell.Current.GoToAsync("..");

A navegação com versões anteriores com ".." também pode ser combinada com uma rota:

await Shell.Current.GoToAsync("../route");

Neste exemplo, a navegação com versões anteriores é executada e, em seguida, a navegação para a rota especificada.

Importante

Navegar para trás e para uma rota especificada só será possível se a navegação com versões anteriores colocar você no local atual na hierarquia de rotas para navegar até a rota especificada.

Da mesma forma, é possível navegar para trás várias vezes e, em seguida, navegar até uma rota especificada:

await Shell.Current.GoToAsync("../../route");

Neste exemplo, a navegação com versões anteriores é executada duas vezes e, em seguida, a navegação para a rota especificada.

Além disso, os dados podem ser passados por meio de propriedades de consulta ao navegar para trás:

await Shell.Current.GoToAsync($"..?parameterToPassBack={parameterValueToPassBack}");

Neste exemplo, a navegação com versões anteriores é executada e o valor do parâmetro de consulta é passado para o parâmetro de consulta na página anterior.

Observação

Os parâmetros de consulta podem ser acrescentados a qualquer solicitação de navegação com versões anteriores.

Para obter mais informações sobre como passar dados ao navegar, consulte Passar dados.

Rotas inválidas

Os seguintes formatos de rota são inválidos:

Formatar Explicação
//página ou ///página No momento, as rotas globais não podem ser a única página na pilha de navegação. Portanto, não há suporte para roteamento absoluto para rotas globais.

O uso desses formatos de rota resulta em um Exception que está sendo gerado.

Aviso

A tentativa de navegar até uma rota não existente resulta na geração de uma exceção ArgumentException.

Depuração de navegação

Algumas das classes de Shell são decoradas com o DebuggerDisplayAttribute, que especifica como uma classe ou um campo é exibido pelo depurador. Isso pode ajudar a depurar solicitações de navegação por meio da exibição de dados relacionados à solicitação de navegação. Por exemplo, a captura de tela a seguir mostra as propriedades CurrentItem e CurrentState do objeto Shell.Current:

Captura de tela do depurador

Neste exemplo, a propriedade CurrentItem, do tipo FlyoutItem, exibe o título e a rota do objeto FlyoutItem. Da mesma forma, a propriedade CurrentState, do tipo ShellNavigationState, exibe o URI da rota exibida no aplicativo do Shell.

A Tab classe define uma Stack propriedade, do tipo IReadOnlyList<Page>, que representa a pilha de navegação atual dentro do Tab. A classe também fornece os seguintes métodos substituíveis de navegação:

  • GetNavigationStack, retorna IReadOnlyList<Page>, a pilha de navegação atual.
  • OnInsertPageBefore, chamado quando INavigation.InsertPageBefore é chamado.
  • OnPopAsync retorna Task<Page> e é chamado quando INavigation.PopAsync é chamado.
  • OnPopToRootAsync retorna Task e é chamado quando INavigation.OnPopToRootAsync é chamado.
  • OnPushAsync retorna Task e é chamado quando INavigation.PushAsync é chamado.
  • OnRemovePage, chamado quando INavigation.RemovePage é chamado.

O exemplo a seguir mostra como substituir o OnRemovePage método :

public class MyTab : Tab
{
    protected override void OnRemovePage(Page page)
    {
        base.OnRemovePage(page);

        // Custom logic
    }
}

Neste exemplo, MyTab os objetos devem ser consumidos na hierarquia visual do Shell em vez de Tab objetos.

A Shell classe define o Navigating evento , que é acionado quando a navegação está prestes a ser executada, devido à navegação programática ou à interação do usuário. O objeto ShellNavigatingEventArgs que acompanha o evento Navigating fornece as seguintes propriedades:

Propriedade Type Descrição
Current ShellNavigationState O URI da página atual.
Source ShellNavigationSource O tipo de navegação que ocorreu.
Target ShellNavigationState O URI que representa para onde a navegação se destina.
CanCancel bool Um valor que indica se é possível cancelar a navegação.
Cancelled bool Um valor que indica se a navegação foi cancelada.

Além disso, a ShellNavigatingEventArgs classe fornece um Cancel método que pode ser usado para cancelar a navegação e um GetDeferral método que retorna um ShellNavigatingDeferral token que pode ser usado para concluir a navegação. Para obter mais informações sobre o adiamento de navegação, consulte Adiamento de navegação.

A Shell classe também define o Navigated evento , que é acionado quando a navegação é concluída. O objeto ShellNavigatedEventArgs que acompanha o evento Navigated fornece as seguintes propriedades:

Propriedade Type Descrição
Current ShellNavigationState O URI da página atual.
Previous ShellNavigationState O URI da página anterior.
Source ShellNavigationSource O tipo de navegação que ocorreu.

Importante

O OnNavigating método é chamado quando o evento é acionado Navigating . Da mesma forma, o OnNavigated método é chamado quando o Navigated evento é acionado. Ambos os métodos podem ser substituídos em sua Shell subclasse para interceptar solicitações de navegação.

As classes ShellNavigatedEventArgs e ShellNavigatingEventArgs têm propriedades Source do tipo ShellNavigationSource. Esta enumeração fornece os seguintes valores:

  • Unknown
  • Push
  • Pop
  • PopToRoot
  • Insert
  • Remove
  • ShellItemChanged
  • ShellSectionChanged
  • ShellContentChanged

Portanto, a navegação pode ser interceptada em uma substituição OnNavigating e as ações podem ser executadas com base na fonte de navegação. Por exemplo, o código a seguir mostra como cancelar a navegação na ordem inversa caso os dados da página não tenham sido salvos:

protected override void OnNavigating(ShellNavigatingEventArgs args)
{
    base.OnNavigating(args);

    // Cancel any back navigation.
    if (args.Source == ShellNavigationSource.Pop)
    {
        args.Cancel();
    }
// }

A navegação do Shell pode ser interceptada e concluída ou cancelada com base na escolha do usuário. Isso pode ser obtido substituindo o OnNavigating método em sua Shell subclasse e chamando o GetDeferral método no ShellNavigatingEventArgs objeto . Esse método retorna um ShellNavigatingDeferral token que tem um Complete método , que pode ser usado para concluir a solicitação de navegação:

public MyShell : Shell
{
    // ...
    protected override async void OnNavigating(ShellNavigatingEventArgs args)
    {
        base.OnNavigating(args);

        ShellNavigatingDeferral token = args.GetDeferral();

        var result = await DisplayActionSheet("Navigate?", "Cancel", "Yes", "No");
        if (result != "Yes")
        {
            args.Cancel();
        }
        token.Complete();
    }    
}

Neste exemplo, uma folha de ações é exibida que convida o usuário a concluir a solicitação de navegação ou cancelá-la. A navegação é cancelada invocando o Cancel método no ShellNavigatingEventArgs objeto . A navegação é concluída invocando o Complete método no ShellNavigatingDeferral token que foi recuperado pelo GetDeferral método no ShellNavigatingEventArgs objeto .

Aviso

O GoToAsync método gerará um InvalidOperationException se um usuário tentar navegar enquanto houver um adiamento de navegação pendente.

Passar dados

Os dados podem ser passados como parâmetros de consulta ao executar a navegação programática baseada em URI. Isso é obtido acrescentando ? após uma rota, seguido por uma ID de parâmetro de consulta, =e um valor. Neste caso, o código a seguir é executado no aplicativo de exemplo quando um usuário seleciona um elefante no ElephantsPage:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
    await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}");
}

Este exemplo de código recupera o elefante selecionado no momento em CollectionView e navega até a rota elephantdetails, passando elephantName como um parâmetro de consulta.

Há duas abordagens para receber dados de navegação:

  1. A classe que representa a página que está sendo navegada ou a classe para a da BindingContextpágina , pode ser decorada com um QueryPropertyAttribute para cada parâmetro de consulta. Para obter mais informações, consulte Processar dados de navegação usando atributos de propriedade de consulta.
  2. A classe que representa a página que está sendo navegada ou a classe para a da BindingContextpágina pode implementar a IQueryAttributable interface. Para obter mais informações, consulte Processar dados de navegação usando um único método.

Processar dados de navegação usando atributos de propriedade de consulta

Os dados de navegação podem ser recebidos decorando a classe receptora com um QueryPropertyAttribute para cada parâmetro de consulta:

[QueryProperty(nameof(Name), "name")]
public partial class ElephantDetailPage : ContentPage
{
    public string Name
    {
        set
        {
            LoadAnimal(value);
        }
    }
    ...

    void LoadAnimal(string name)
    {
        try
        {
            Animal animal = ElephantData.Elephants.FirstOrDefault(a => a.Name == name);
            BindingContext = animal;
        }
        catch (Exception)
        {
            Console.WriteLine("Failed to load animal.");
        }
    }    
}

O primeiro argumento para o QueryPropertyAttribute especifica o nome da propriedade que receberá os dados, com o segundo argumento especificando a ID do parâmetro de consulta. Portanto, o QueryPropertyAttribute no exemplo acima especifica que a Name propriedade receberá os dados passados no name parâmetro de consulta do URI na chamada de GoToAsync método. O Name setter de propriedade chama o LoadAnimal método para recuperar o Animal objeto para o e o namedefine como o BindingContext da página.

Observação

Os valores de parâmetro de consulta recebidos por meio do QueryPropertyAttribute são decodificados automaticamente pela URL.

Processar dados de navegação usando um único método

Os dados de navegação podem ser recebidos implementando a IQueryAttributable interface na classe de recebimento. A IQueryAttributable interface especifica que a classe de implementação deve implementar o ApplyQueryAttributes método . Esse método tem um query argumento, do tipo IDictionary<string, string>, que contém todos os dados passados durante a navegação. Cada chave no dicionário é uma ID de parâmetro de consulta, com seu valor sendo o valor do parâmetro de consulta. A vantagem de usar essa abordagem é que os dados de navegação podem ser processados usando um único método, o que pode ser útil quando você tem vários itens de dados de navegação que exigem processamento como um todo.

O exemplo a seguir mostra uma classe de modelo de exibição que implementa a IQueryAttributable interface :

public class MonkeyDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Monkey { get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, string> query)
    {
        // The query parameter requires URL decoding.
        string name = HttpUtility.UrlDecode(query["name"]);
        LoadAnimal(name);
    }

    void LoadAnimal(string name)
    {
        try
        {
            Monkey = MonkeyData.Monkeys.FirstOrDefault(a => a.Name == name);
            OnPropertyChanged("Monkey");
        }
        catch (Exception)
        {
            Console.WriteLine("Failed to load animal.");
        }
    }
    ...
}

Neste exemplo, o ApplyQueryAttributes método recupera o valor do parâmetro de name consulta do URI na chamada de GoToAsync método. Em seguida, o LoadAnimal método é chamado para recuperar o Animal objeto , no qual é definido como o valor da propriedade à qual os Monkey dados estão associados.

Importante

Os valores de parâmetro de consulta recebidos por meio da IQueryAttributable interface não são decodificados automaticamente pela URL.

Passar e processar vários parâmetros de consulta

Vários parâmetros de consulta podem ser passados conectando-os com &. Por exemplo, o código a seguir passa dois itens de dados:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
    string elephantLocation = (e.CurrentSelection.FirstOrDefault() as Animal).Location;
    await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}&location={elephantLocation}");
}

Este exemplo de código recupera o elefante selecionado no momento no CollectionViewe navega até a elephantdetails rota, passando elephantName e elephantLocation como parâmetros de consulta.

Para receber vários itens de dados, a classe que representa a página que está sendo navegada ou a classe para o da BindingContextpágina pode ser decorada com um QueryPropertyAttribute para cada parâmetro de consulta:

[QueryProperty(nameof(Name), "name")]
[QueryProperty(nameof(Location), "location")]
public partial class ElephantDetailPage : ContentPage
{
    public string Name
    {
        set
        {
            // Custom logic
        }
    }

    public string Location
    {
        set
        {
            // Custom logic
        }
    }
    ...    
}

Neste exemplo, a classe é decorada com um QueryPropertyAttribute para cada parâmetro de consulta. O primeiro QueryPropertyAttribute especifica que a Name propriedade receberá os dados passados no name parâmetro de consulta, enquanto o segundo QueryPropertyAttribute especifica que a Location propriedade receberá os dados passados no location parâmetro de consulta. Em ambos os casos, os valores de parâmetro de consulta são especificados no URI na chamada de GoToAsync método.

Como alternativa, os dados de navegação podem ser processados por um único método implementando a IQueryAttributable interface na classe que representa a página que está sendo navegada ou a classe para a da página BindingContext:

public class ElephantDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Elephant { get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, string> query)
    {
        string name = HttpUtility.UrlDecode(query["name"]);
        string location = HttpUtility.UrlDecode(query["location"]);
        ...        
    }
    ...
}

Neste exemplo, o ApplyQueryAttributes método recupera o valor dos name parâmetros de consulta e location do URI na chamada de GoToAsync método.

Comportamento do botão Voltar

A aparência e o comportamento do botão Voltar podem ser redefinidos definindo a BackButtonBehavior propriedade anexada como um BackButtonBehavior objeto . A BackButtonBehavior classe define as seguintes propriedades:

  • Command, do tipo ICommand, que é executado quando o botão Voltar é pressionado.
  • CommandParameter, do tipo object, que é o parâmetro passado para Command.
  • IconOverride, do tipo ImageSource, o ícone usado para o botão Voltar.
  • IsEnabled, do tipo boolean, indica se o botão Voltar está habilitado. O valor padrão é true.
  • TextOverride, do tipo string, o texto usado para o botão Voltar.

Todas essas propriedades são apoiadas por objetos BindableProperty, o que significa que essas propriedades podem ser o destino de vinculações de dados.

O código a seguir mostra um exemplo de redefinição da aparência e do comportamento do botão voltar:

<ContentPage ...>    
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding BackCommand}"
                            IconOverride="back.png" />   
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

Este é o código C# equivalente:

Shell.SetBackButtonBehavior(this, new BackButtonBehavior
{
    Command = new Command(() =>
    {
        ...
    }),
    IconOverride = "back.png"
});

A propriedade Command é definida como uma ICommand a ser executada quando o botão Voltar é pressionado, e a propriedade IconOverride é definida como o ícone usado pelo botão Voltar:

Captura de tela de uma substituição do ícone do botão Voltar do Shell no iOS e no Android