Navegação
Gorjeta
Este conteúdo é um excerto do eBook, Enterprise Application Patterns Using .NET MAUI, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
O .NET MAUI inclui suporte para navegação de página, que normalmente resulta da interação do usuário com a interface do usuário ou do próprio aplicativo como resultado de alterações de estado internas orientadas por lógica. No entanto, a navegação pode ser complexa de implementar em aplicativos que usam o padrão Model-View-ViewModel (MVVM), pois os seguintes desafios devem ser enfrentados:
- Identificar o modo de exibição a ser navegado usando uma abordagem que não introduza acoplamento rígido e dependências entre modos de exibição.
- Coordenar o processo pelo qual a exibição a ser navegada é instanciada e inicializada. Ao usar o MVVM, o modo de exibição e o modelo de exibição precisam ser instanciados e associados um ao outro por meio do contexto de vinculação do modo de exibição. Quando um aplicativo está usando um contêiner de injeção de dependência, a instanciação de modos de exibição e modelos de exibição pode exigir um mecanismo de construção específico.
- Se a navegação view-first deve ser executada ou view-model-first. Com a navegação view-first, a página para a qual navegar refere-se ao nome do tipo de visualização. Durante a navegação, a exibição especificada é instanciada, juntamente com seu modelo de exibição correspondente e outros serviços dependentes. Uma abordagem alternativa é usar a navegação view-model-first, em que a página para navegar se refere ao nome do tipo view-model.
- Determinar como separar de forma clara o comportamento de navegação do aplicativo entre os modos de exibição e os modelos de exibição. O padrão MVVM separa a interface do usuário do aplicativo e sua apresentação e lógica de negócios, mas não fornece um mecanismo direto para vinculá-los. No entanto, o comportamento de navegação de um aplicativo geralmente abrange as partes da interface do usuário e da apresentação do aplicativo. O usuário geralmente iniciará a navegação a partir de um modo de exibição, e o modo de exibição será substituído como resultado da navegação. No entanto, muitas vezes, a navegação também pode precisar ser iniciada ou coordenada a partir do modelo de exibição.
- Determinar como passar parâmetros durante a navegação para fins de inicialização. Por exemplo, se o usuário navegar para uma exibição para atualizar os detalhes do pedido, os dados do pedido terão que ser passados para a exibição para que ela possa exibir os dados corretos.
- Coordenar a navegação para garantir que as regras de negócio específicas são obedecidas. Por exemplo, os usuários podem ser solicitados antes de navegar para fora de um modo de exibição para que possam corrigir quaisquer dados inválidos ou ser solicitados a enviar ou descartar quaisquer alterações de dados que foram feitas no modo de exibição.
Este capítulo aborda esses desafios apresentando uma classe de serviço de navegação chamada MauiNavigationService
que é usada para executar a navegação de primeira página de modelo de exibição.
Nota
O MauiNavigationService
usado pelo aplicativo é simplista e não abrange todos os tipos de navegação possíveis. Os tipos de navegação necessários para o seu aplicativo podem exigir funcionalidade adicional.
Navegar entre páginas
A lógica de navegação pode residir no code-behind de um modo de exibição ou em um modelo de exibição vinculado a dados. Embora colocar a lógica de navegação em uma exibição possa ser a abordagem mais direta, ela não é facilmente testável por meio de testes de unidade. Colocar a lógica de navegação em classes de modelo de exibição significa que a lógica pode ser verificada por meio de testes de unidade. Além disso, o modelo de exibição pode implementar lógica para controlar a navegação para garantir que determinadas regras de negócios sejam aplicadas. Por exemplo, um aplicativo pode não permitir que o usuário navegue para fora de uma página sem primeiro garantir que os dados inseridos sejam válidos.
Um serviço de navegação é normalmente invocado a partir de modelos de visualização, a fim de promover a estabilidade. No entanto, navegar para modos de exibição a partir de modelos de exibição exigiria que os modelos de exibição fizessem referência a modos de exibição e, particularmente, modos de exibição aos quais o modelo de exibição ativo não está associado, o que não é recomendado. Portanto, o MauiNavigationService
apresentado aqui especifica o tipo de modelo de exibição como o destino para o qual navegar.
O aplicativo multiplataforma eShop usa a MauiNavigationService
classe para fornecer navegação view-model-first. Essa classe implementa a INavigationService
interface, que é mostrada no exemplo de código a seguir:
public interface INavigationService
{
Task InitializeAsync();
Task NavigateToAsync(string route, IDictionary<string, object> routeParameters = null);
Task PopAsync();
}
Essa interface especifica que uma classe de implementação deve fornecer os seguintes métodos:
Método | Objetivo |
---|---|
InitializeAsync |
Executa a navegação para uma das duas páginas quando o aplicativo é iniciado. |
NavigateToAsync(string route, IDictionary<string, object> routeParameters = null) |
Executa a navegação hierárquica para uma página especificada usando uma rota de navegação registrada. Opcionalmente, pode passar parâmetros de rota nomeados para uso no processamento na página de destino |
PopAsync |
Remove a página atual da pilha de navegação. |
Nota
Uma INavigationService
interface geralmente também especificaria um GoBackAsync
método, que é usado para retornar programaticamente à página anterior na pilha de navegação. No entanto, esse método está ausente do aplicativo multiplataforma eShop porque não é necessário.
Criando a instância MauiNavigationService
A MauiNavigationService
classe, que implementa a INavigationService
interface, é registrada como um singleton com o contêiner de injeção de dependência no MauiProgram.CreateMauiApp()
método, conforme demonstrado no exemplo de código a seguir:
mauiAppBuilder.Services.AddSingleton<INavigationService, MauiNavigationService>();;
A INavigationService
interface pode então ser resolvida adicionando-a ao construtor de nossos modos de exibição e modelos de visualização, conforme demonstrado no exemplo de código a seguir:
public AppShell(INavigationService navigationService)
Isso retorna uma referência ao MauiNavigationService
objeto armazenado no contêiner de injeção de dependência.
A ViewModelBase
classe armazena a MauiNavigationService
instância em uma NavigationService
propriedade, do tipo INavigationService
. Portanto, todas as classes view-model, que derivam da ViewModelBase
classe, podem usar a NavigationService
propriedade para acessar os métodos especificados pela INavigationService
interface.
Tratamento de pedidos de navegação
O .NET MAUI fornece várias maneiras de navegar dentro de um aplicativo. A maneira tradicional de navegar é com a NavigationPage
classe, que implementa uma experiência de navegação hierárquica na qual o usuário pode navegar pelas páginas, para frente e para trás, conforme desejado. O aplicativo eShop usa o Shell
componente como o contêiner raiz para o aplicativo e como um host de navegação. Para obter mais informações sobre a navegação do Shell, consulte Navegação do Shell no Microsoft Developer Center.
A navegação é realizada dentro de classes view-model invocando um dos NavigateToAsync
métodos, especificando o caminho de rota para a página para a qual está sendo navegada, conforme demonstrado no exemplo de código a seguir:
await NavigationService.NavigateToAsync("//Main");
O exemplo de MauiNavigationService
código a seguir mostra o NavigateToAsync
método fornecido pela classe:
public Task NavigateToAsync(string route, IDictionary<string, object> routeParameters = null)
{
return
routeParameters != null
? Shell.Current.GoToAsync(route, routeParameters)
: Shell.Current.GoToAsync(route);
}
O controle .NET MAUIShell
já está familiarizado com a navegação baseada em rota, portanto, o NavigateToAsync
método funciona para mascarar essa funcionalidade. O NavigateToAsync
método permite que os dados de navegação sejam especificados como um argumento que é passado para o modelo de exibição para o qual está sendo navegado, onde normalmente é usado para executar a inicialização. Para obter mais informações, consulte Passando parâmetros durante a navegação.
Importante
Há várias maneiras de executar a navegação no .NET MAUI. O MauiNavigationService
é especificamente construído para trabalhar com Shell
. Se você estiver usando um NavigationPage
ou TabbedPage
um mecanismo de navegação diferente, esse serviço de roteamento terá que ser atualizado para funcionar usando esses componentes.
Para registrar rotas para o MauiNavigationService
precisamos fornecer informações de rota de XAML ou no code-behind. O exemplo a seguir mostra o registro de rotas via XAML.
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:eShop.Views"
x:Class="eShop.AppShell">
<!-- Omitted for brevity -->
<FlyoutItem >
<ShellContent x:Name="login" ContentTemplate="{DataTemplate views:LoginView}" Route="Login" />
</FlyoutItem>
<TabBar x:Name="main" Route="Main">
<ShellContent Title="CATALOG" Route="Catalog" Icon="{StaticResource CatalogIconImageSource}" ContentTemplate="{DataTemplate views:CatalogView}" />
<ShellContent Title="PROFILE" Route="Profile" Icon="{StaticResource ProfileIconImageSource}" ContentTemplate="{DataTemplate views:ProfileView}" />
</TabBar>
</Shell>
Neste exemplo, os objetos e TabBar
a interface do ShellContent
usuário estão definindo sua Route
propriedade. Este é o método preferencial de registro de rotas para objetos de interface do usuário que são controlados por um Shell
arquivo .
Se tivermos objetos que serão adicionados à pilha de navegação posteriormente, precisaremos adicioná-los via code-behind. O exemplo a seguir mostra o registro de rotas em code-behind.
Routing.RegisterRoute("Filter", typeof(FiltersView));
Routing.RegisterRoute("Basket", typeof(BasketView));
No code-behind, chamaremos o Routing.RegisterRoute
método que usa um nome de rota como o primeiro parâmetro e um tipo de exibição como o segundo parâmetro. Quando um modelo de exibição usa a NavigationService
propriedade para navegar, o objeto do Shell
aplicativo procurará rotas registradas e as enviará para a pilha de navegação.
Depois que o modo de exibição é criado e navegado, os ApplyQueryAttributes
métodos e InitializeAsync
do modelo de exibição associado ao modo de exibição são executados. Para obter mais informações, consulte Passando parâmetros durante a navegação.
Navegar quando a aplicação é iniciada
Quando o aplicativo é iniciado, um Shell
objeto é definido como a exibição raiz do aplicativo. Uma vez definido, o será usado para controlar o Shell
registro de rota e estará presente na raiz do nosso aplicativo daqui para frente. Shell
Uma vez criado, podemos esperar que ele seja anexado ao aplicativo usando o OnParentSet
método para inicializar nossa rota de navegação. O exemplo de código a seguir mostra esse método:
protected override async void OnParentSet()
{
base.OnParentSet();
if (Parent is not null)
{
await _navigationService.InitializeAsync();
}
}
O método usa uma instância da INavigationService
qual é fornecido o construtor da injeção de dependência e invoca seu InitializeAsync
método.
O exemplo de código a seguir mostra a implementação do MauiNavigationService.InitializeAsync
método:
public Task InitializeAsync()
{
return NavigateToAsync(string.IsNullOrEmpty(_settingsService.AuthAccessToken)
? "//Login"
: "//Main/Catalog");
}
A //Main/Catalog
rota é navegada para se o aplicativo tiver um token de acesso em cache, que é usado para autenticação. Caso contrário, a //Login
rota é navegada para.
Passando parâmetros durante a navegação
O NavigateToAsync
método, especificado pela interface, permite que os INavigationService
dados de navegação sejam especificados como um IDictionary<string, object>
dos dados que são passados para o modelo de exibição para o qual estão sendo navegados, onde normalmente são usados para executar a inicialização.
Por exemplo, a ProfileViewModel
classe contém um OrderDetailCommand
que é executado quando o usuário seleciona uma ordem na ProfileView
página. Por sua vez, isso executa o OrderDetailAsync
método, que é mostrado no exemplo de código a seguir:
private async Task OrderDetailAsync(Order order)
{
if (order is null)
{
return;
}
await NavigationService.NavigateToAsync(
"OrderDetail",
new Dictionary<string, object>{ { "OrderNumber", order.OrderNumber } });
}
Este método invoca a navegação para a OrderDetail
rota, passando informações de número de ordem na ordem que o usuário selecionou. Quando a estrutura de injeção de dependência cria o OrderDetailView
para a OrderDetail
rota junto com a OrderDetailViewModel
classe que é atribuída ao modo de exibição BindingContext
. O OrderDetailViewModel
tem um atributo adicionado a ele que lhe permite receber dados do serviço de navegação, conforme mostrado no exemplo de código abaixo.
[QueryProperty(nameof(OrderNumber), "OrderNumber")]
public class OrderDetailViewModel : ViewModelBase
{
public int OrderNumber { get; set; }
}
O QueryProperty
atributo nos permite fornecer um parâmetro para uma propriedade para mapear valores e uma chave para encontrar valores do dicionário de parâmetros de consulta. Neste exemplo, a chave "OrderNumber" e o valor do número do pedido foram fornecidos durante a NavigateToAsync
chamada. O modelo de visualização encontrou a chave "OrderNumber" e mapeou o valor para a OrderNumber
propriedade. A OrderNumber
propriedade pode ser usada posteriormente para recuperar os detalhes completos do pedido da OrderService
instância.
Invocando a navegação usando comportamentos
A navegação geralmente é acionada a partir de uma exibição por uma interação do usuário. Por exemplo, o executa a navegação após a LoginView
autenticação bem-sucedida. O exemplo de código a seguir mostra como a navegação é invocada por um comportamento:
<WebView>
<WebView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="Navigating"
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</WebView.Behaviors>
</WebView>
No tempo de execução, o EventToCommandBehavior
responderá à interação com o WebView
. Quando o WebView
navega para uma página da web, o Navigating
evento será acionado, o que executará o NavigateCommand
no LoginViewMode
l. Por padrão, os argumentos de evento para o evento são passados para o comando. Esses dados são convertidos à medida que são passados entre a origem e o destino pelo conversor especificado na EventArgsConverter
propriedade, que retorna o Url
do WebNavigatingEventArgs
. Portanto, quando o NavigationCommand
é executado, o Url
da página da Web é passado como um parâmetro para a Ação registrada.
Por sua vez, o NavigationCommand
executa o NavigateAsync
método, que é mostrado no exemplo de código a seguir:
private async Task NavigateAsync(string url)
{
// Omitted for brevity.
if (!string.IsNullOrWhiteSpace(accessToken))
{
_settingsService.AuthAccessToken = accessToken;
_settingsService.AuthIdToken = authResponse.IdentityToken;
await NavigationService.NavigateToAsync("//Main/Catalog");
}
}
Esse método invoca o roteamento NavigationService
do aplicativo para a //Main/Catalog
rota.
Confirmar ou cancelar a navegação
Um aplicativo pode precisar interagir com o usuário durante uma operação de navegação, para que o usuário possa confirmar ou cancelar a navegação. Isso pode ser necessário, por exemplo, quando o usuário tenta navegar antes de ter concluído completamente uma página de entrada de dados. Nessa situação, um aplicativo deve fornecer uma notificação que permita ao usuário navegar para fora da página ou cancelar a operação de navegação antes que ela ocorra. Isso pode ser feito em uma classe view-model usando a resposta de uma notificação para controlar se a navegação é invocada ou não.
Resumo
O .NET MAUI inclui suporte para navegação de página, que normalmente resulta da interação do usuário com a interface do usuário ou do próprio aplicativo, como resultado de alterações de estado orientadas por lógica interna. No entanto, a navegação pode ser complexa de implementar em aplicativos que usam o padrão MVVM.
Este capítulo apresentou uma classe NavigationService, que é usada para executar a navegação view-model-first a partir de view-models. Colocar a lógica de navegação em classes de modelo de exibição significa que a lógica pode ser exercida por meio de testes automatizados. Além disso, o modelo de exibição pode implementar lógica para controlar a navegação para garantir que determinadas regras de negócios sejam aplicadas.