Partilhar via


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.

Padrões de aplicativos corporativos usando a miniatura da capa do eBook .NET MAUI .

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.

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

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.

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