Xamarin.Forms Navegação do shell
Xamarin.Forms O Shell inclui uma experiência de navegação baseada em URI que usa rotas para navegar para 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 classe Shell
define as seguintes propriedades relacionadas à navegação:
BackButtonBehavior
, do tipoBackButtonBehavior
, uma propriedade anexada que define o comportamento do botão Voltar.CurrentItem
, do tipoShellItem
, o item atualmente selecionado.CurrentPage
, do tipoPage
, a página apresentada no momento.CurrentState
, do tipoShellNavigationState
, o estado de navegação atual doShell
.Current
, do tipoShell
, um alias convertido em tipo paraApplication.Current.MainPage
.
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 evento Navigating
é acionado. Quando a navegação é concluída, o evento Navigated
é acionado.
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 nos objetos FlyoutItem
, TabBar
, Tab
e ShellContent
por meio das propriedades Route
:
<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, ela 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 as rotas da 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 as páginas de detalhes, que não são definidas na subclasse Shell
, como rotas. Essas páginas de detalhes podem ser navegadas 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, confira 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 objeto Shell
pode ser recuperado por meio da propriedade Shell.Current.CurrentState
, que inclui o URI da rota exibida na propriedade Location
.
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 rota especificada será pesquisada na hierarquia de rotas, no sentido ascendente em relação à posição atual. A página correspondente será enviada por push para a pilha de navegação. |
/route | A hierarquia de rotas será pesquisada começando na rota especificada, no sentido descendente em relação à posição atual. A página correspondente será enviada por push para a pilha de navegação. |
//route | A rota especificada será pesquisada na hierarquia de rotas, no sentido ascendente em relação à posição atual. A página correspondente substituirá a pilha de navegação. |
///route | A rota especificada será pesquisada na hierarquia de rotas, no sentido descendente em relação à posição atual. A página correspondente substituirá a pilha de navegação. |
O seguinte exemplo mostra como navegar até a página da rota monkeydetails
:
await Shell.Current.GoToAsync("monkeydetails");
Neste exemplo, a rota monkeyDetails
é pesquisada na hierarquia, no sentido descendente, até que a página correspondente seja encontrada. Quando a página é encontrada, ela é enviada 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 regressiva com “..” também pode ser combinada com uma rota:
await Shell.Current.GoToAsync("../route");
Neste exemplo, a navegação regressiva é executada e, em seguida, a navegação para a rota especificada.
Importante
Navegar de maneira regressiva e para uma rota especificada só será possível se a navegação regressiva colocar você no local atual na hierarquia de rotas para navegar até a rota especificada.
Da mesma forma, é possível navegar de maneira regressiva várias vezes e, em seguida, navegar para uma rota especificada:
await Shell.Current.GoToAsync("../../route");
Neste exemplo, a navegação regressiva é executada duas vezes e, em seguida, a navegação para a rota especificada.
Além disso, os dados podem ser transmitidos pelas propriedades da consulta durante a navegação regressiva:
await Shell.Current.GoToAsync($"..?parameterToPassBack={parameterValueToPassBack}");
Neste exemplo, a navegação regressiva é executada e o valor do parâmetro de consulta é transmitido 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 regressiva.
Para obter mais informações sobre como transmitir dados durante a navegação, confira Transmitir 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 na geração de uma Exception
.
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
:
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.
Pilha de navegação
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
, retornaIReadOnlyList<Page>
, a pilha de navegação atual.OnInsertPageBefore
, chamado quandoINavigation.InsertPageBefore
é chamado.OnPopAsync
retornaTask<Page>
e é chamado quandoINavigation.PopAsync
é chamado.OnPopToRootAsync
retornaTask
e é chamado quandoINavigation.OnPopToRootAsync
é chamado.OnPushAsync
retornaTask
e é chamado quandoINavigation.PushAsync
é chamado.OnRemovePage
, chamado quandoINavigation.RemovePage
é chamado.
O seguinte exemplo mostra como substituir o método OnRemovePage
:
public class MyTab : Tab
{
protected override void OnRemovePage(Page page)
{
base.OnRemovePage(page);
// Custom logic
}
}
Neste exemplo, os objetos MyTab
devem ser consumidos na hierarquia visual do Shell em vez dos objetos Tab
.
Eventos de navegação
A classe Shell
define o evento Navigating
, 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 classe ShellNavigatingEventArgs
fornece um método Cancel
que pode ser usado para cancelar a navegação e um método GetDeferral
que retorna um token ShellNavigatingDeferral
que pode ser usado para concluir a navegação. Para obter mais informações sobre o adiamento de navegação, confira Adiamento da navegação.
A classe Shell
também define o evento Navigated
, 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 método OnNavigating
é chamado quando o evento Navigating
é acionado. Da mesma forma, o método OnNavigated
é chamado quando o evento Navigated
é acionado. Os dois métodos podem ser substituídos na subclasse Shell
para interceptar as 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 origem da 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();
}
// }
Diferimento de navegação
A navegação do shell pode ser interceptada e concluída ou cancelada de acordo com a escolha do usuário. Isso pode ser feito com a substituição do método OnNavigating
na subclasse Shell
e com a chamada do método GetDeferral
no objeto ShellNavigatingEventArgs
. Esse método retorna um token ShellNavigatingDeferral
que tem um método Complete
, 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, é exibida uma folha de ação que convida o usuário a concluir a solicitação de navegação ou cancelá-la. A navegação é cancelada com a invocação do método Cancel
no objeto ShellNavigatingEventArgs
. A navegação é concluída com a invocação do método Complete
no token ShellNavigatingDeferral
que foi recuperado pelo método GetDeferral
no objeto ShellNavigatingEventArgs
.
Aviso
O método GoToAsync
vai gerar uma 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 anexando após uma rota, seguida 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 atualmente selecionado em CollectionView
e navega até a rota elephantdetails
, passando elephantName
como um parâmetro de consulta.
Há duas abordagens para o recebimento dos dados de navegação:
- A classe que representa a página que está sendo acessada ou a classe do
BindingContext
da página pode ser decorada com umQueryPropertyAttribute
para cada parâmetro de consulta. Para obter mais informações, confira Processar os dados de navegação usando atributos de propriedade de consulta. - A classe que representa a página para a qual está sendo acessada ou a classe do
BindingContext
da página, pode implementar a interfaceIQueryAttributable
. Para obter mais informações, confira Processar os dados de navegação usando um só método.
Processar os dados de navegação usando atributos de propriedade de consulta
Os dados de navegação podem ser recebidos decorando a classe de recebimento com um QueryPropertyAttribute
parâmetro de consulta para cada:
[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 o 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 name
, e o define como o BindingContext
da página.
Observação
Os valores de parâmetro de consulta recebidos por meio do QueryPropertyAttribute
são decodificados automaticamente por URL.
Processar os dados de navegação usando um só método
Os dados de navegação podem ser recebidos com a implementação da interface IQueryAttributable
na classe de recebimento. A interface IQueryAttributable
especifica que a classe de implementação precisa implementar o método ApplyQueryAttributes
. Esse método tem um argumento query
, do tipo IDictionary<string, string>
, que contém todos os dados transmitidos 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 com um só método, o que pode ser útil quando você tem vários itens de dados de navegação que exigem o processamento como um todo.
O seguinte exemplo mostra uma classe de modelo de exibição que implementa a interface IQueryAttributable
:
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, onde ele é definido como o valor da Monkey
propriedade à qual os dados estão associados.
Importante
Os valores de parâmetro de consulta recebidos por meio da IQueryAttributable
interface não são decodificados automaticamente por 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 seguinte código transmite 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 atualmente selecionado no CollectionView
e navega até a rota elephantdetails
, transmitindo 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 da BindingContext
página , pode ser decorada com um QueryPropertyAttribute
parâmetro de consulta for each:
[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 propriedade Name
receberá os dados transmitidos no parâmetro de consulta name
, enquanto o segundo QueryPropertyAttribute
especifica que a propriedade Location
receberá os dados transmitidos no parâmetro de consulta location
. Em ambos os casos, os valores de parâmetro de consulta são especificados no URI na chamada de método GoToAsync
.
Como alternativa, os dados de navegação podem ser processados por um só método, implementando a interface IQueryAttributable
na classe que representa a página que está sendo acessada ou a classe do BindingContext
da página:
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 método ApplyQueryAttributes
recupera o valor dos parâmetros de consulta name
e location
do URI na chamada de método GoToAsync
.
Comportamento do botão Voltar
A aparência e o comportamento do botão Voltar podem ser redefinidos com a definição da propriedade anexada BackButtonBehavior
como um objeto BackButtonBehavior
. A classe BackButtonBehavior
define as seguintes propriedades:
Command
, do tipoICommand
, que é executado quando o botão Voltar é pressionado.CommandParameter
, do tipoobject
, que é o parâmetro passado paraCommand
.IconOverride
, do tipoImageSource
, o ícone usado para o botão Voltar.IsEnabled
, do tipoboolean
, indica se o botão Voltar está habilitado. O valor padrão étrue
.TextOverride
, do tipostring
, 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 seguinte código 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: