Partage via


Navigation d’application d’entreprise

Remarque

Ce livre électronique a été publié au printemps 2017 et n’a pas été mis à jour depuis. Il y a beaucoup dans le livre qui reste précieux, mais certains de la matière est obsolète.

Xamarin.Forms inclut la prise en charge de la navigation dans les pages, qui résulte généralement de l’interaction de l’utilisateur avec l’interface utilisateur ou de l’application elle-même en raison des modifications d’état basées sur la logique interne. Toutefois, la navigation peut être complexe à implémenter dans les applications qui utilisent le modèle MVVM (modèle-vue-vue modèle), car les difficultés suivantes doivent être aplanies :

  • Comment identifier la vue à accéder à une approche qui n’introduit pas de couplage étroit et de dépendances entre les vues.
  • Comment coordonner le processus par lequel la vue à accéder est instanciée et initialisée. Lorsque vous utilisez MVVM, le modèle d’affichage et de vue doit être instancié et associé les uns aux autres via le contexte de liaison de la vue. Lorsqu’une application utilise un conteneur d’injection de dépendances, l’instanciation des vues et des modèles de vue peut nécessiter un mécanisme de construction spécifique.
  • Indique s’il faut effectuer une navigation d’affichage en premier ou afficher la première navigation de modèle. Avec la navigation basée sur la vue en premier, la page cible de la navigation fait référence au nom du type de vue. Pendant la navigation, la vue spécifiée est instanciée, ainsi que son modèle d’affichage correspondant et d’autres services dépendants. Une autre approche consiste à utiliser la navigation d’affichage du modèle d’abord, où la page à parcourir fait référence au nom du type de modèle d’affichage.
  • Comment séparer correctement le comportement de navigation de l’application entre les vues et les modèles d’affichage. Le modèle MVVM fournit une séparation entre l’interface utilisateur de l’application et sa présentation et sa logique métier. Toutefois, le comportement de navigation d’une application s’étend souvent sur l’interface utilisateur et les parties présentations de l’application. L’utilisateur lance souvent la navigation à partir d’une vue, laquelle est remplacée à la suite de la navigation. Toutefois, la navigation peut souvent avoir besoin d’être initiée ou coordonnée à partir du modèle d’affichage.
  • Comment passer des paramètres pendant la navigation à des fins d’initialisation. Par exemple, si l’utilisateur accède à une vue pour mettre à jour les détails d’une commande, les informations relatives à la commande doivent être passées à la vue pour qu’elle puisse afficher les données appropriées.
  • Comment coordonner la navigation afin de garantir que certaines règles d’entreprise sont respectées. Par exemple, avant de quitter une vue, les utilisateurs peuvent être invités à corriger les données non valides, ou à envoyer ou abandonner les changements apportés aux données dans la vue.

Ce chapitre traite de ces défis en présentant une NavigationService classe utilisée pour afficher la navigation de la première page du modèle.

Remarque

L’application NavigationService est conçue uniquement pour effectuer une navigation hiérarchique entre les instances ContentPage. L’utilisation du service pour naviguer entre d’autres types de pages peut entraîner un comportement inattendu.

La logique de navigation peut résider dans le code-behind d’une vue ou dans un modèle de vue lié aux données. Lorsque vous placez la logique de navigation dans une vue, il peut s’agir de l’approche la plus simple, mais elle n’est pas facilement testable par le biais de tests unitaires. Placer la logique de navigation dans les classes de modèle d’affichage signifie que la logique peut être exercée via des tests unitaires. En outre, le modèle d’affichage peut ensuite implémenter la logique pour contrôler la navigation afin de s’assurer que certaines règles d’entreprise sont appliquées. Par exemple, une application peut interdire à l’utilisateur de quitter une page tant que la validité des données entrées n’a pas été vérifiée.

Une NavigationService classe est généralement appelée à partir de modèles d’affichage pour promouvoir la testabilité. Toutefois, la navigation vers des vues à partir de modèles d’affichage nécessite que les modèles d’affichage référencent des vues, et en particulier les vues auxquelles le modèle d’affichage actif n’est pas associé, ce qui n’est pas recommandé. Par conséquent, le NavigationService modèle présenté ici spécifie le type de modèle d’affichage en tant que cible vers laquelle accéder.

L’application mobile eShopOnContainers utilise la NavigationService classe pour fournir une navigation model-first. Cette classe implémente l’interface INavigationService, comme le montre l’exemple de code suivant :

public interface INavigationService  
{  
    ViewModelBase PreviousPageViewModel { get; }  
    Task InitializeAsync();  
    Task NavigateToAsync<TViewModel>() where TViewModel : ViewModelBase;  
    Task NavigateToAsync<TViewModel>(object parameter) where TViewModel : ViewModelBase;  
    Task RemoveLastFromBackStackAsync();  
    Task RemoveBackStackAsync();  
}

Cette interface spécifie qu’une classe d’implémentation doit fournir les méthodes suivantes :

Méthode Objectif
InitializeAsync Effectue la navigation vers une page au choix parmi deux au lancement de l’application.
NavigateToAsync Effectue une navigation hiérarchique vers une page spécifiée.
NavigateToAsync(parameter) Effectue une navigation hiérarchique vers une page spécifiée, en passant un paramètre.
RemoveLastFromBackStackAsync Supprime la page précédente de la pile de navigation.
RemoveBackStackAsync Supprime toutes les pages précédentes de la pile de navigation.

En outre, l’interface INavigationService spécifie qu’une classe d’implémentation doit fournir une PreviousPageViewModel propriété. Cette propriété retourne le type de modèle d’affichage associé à la page précédente dans la pile de navigation.

Remarque

Une interface INavigationService spécifie également une méthode GoBackAsync, qui permet de retourner par programmation à la page précédente dans la pile de navigation. Toutefois, cette méthode est manquante dans l’application mobile eShopOnContainers, car elle n’est pas requise.

Création de l’instance NavigationService

La NavigationService classe, qui implémente l’interface INavigationService , est inscrite en tant que singleton avec le conteneur d’injection de dépendances Autofac, comme illustré dans l’exemple de code suivant :

builder.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();

L’interface INavigationService est résolue dans le ViewModelBase constructeur de classe, comme illustré dans l’exemple de code suivant :

NavigationService = ViewModelLocator.Resolve<INavigationService>();

Cette opération renvoie une référence à l’objet NavigationService stocké dans le conteneur d’injection de dépendances Autofac, qui est créé par la InitNavigation méthode de la App classe. Pour plus d’informations, consultez Navigation lors du lancement de l’application.

La classe ViewModelBase stocke l’instance de NavigationService dans une propriété NavigationService, de type INavigationService. Par conséquent, toutes les classes de modèle d’affichage, qui dérivent de la ViewModelBase classe, peuvent utiliser la NavigationService propriété pour accéder aux méthodes spécifiées par l’interface INavigationService . Cela évite la surcharge liée à l’injection de l’objet NavigationService à partir du conteneur d’injection de dépendances Autofac dans chaque classe de modèle d’affichage.

Gestion des demandes de navigation

Xamarin.Forms fournit la NavigationPage classe, qui implémente une expérience de navigation hiérarchique dans laquelle l’utilisateur peut parcourir les pages, les avants et les arrières, comme vous le souhaitez. Pour plus d’informations sur la navigation hiérarchique, consultez Navigation hiérarchique.

Au lieu d’utiliser la NavigationPage classe directement, l’application eShopOnContainers encapsule la NavigationPage classe dans la CustomNavigationView classe, comme illustré dans l’exemple de code suivant :

public partial class CustomNavigationView : NavigationPage  
{  
    public CustomNavigationView() : base()  
    {  
        InitializeComponent();  
    }  

    public CustomNavigationView(Page root) : base(root)  
    {  
        InitializeComponent();  
    }  
}

L’objectif de ce wrapping est de faciliter le style de l’instance NavigationPage à l’intérieur du fichier XAML pour la classe.

La navigation est effectuée à l’intérieur des classes de modèle d’affichage en appelant l’une des méthodes, en spécifiant le type de NavigateToAsync modèle d’affichage de la page à accéder, comme illustré dans l’exemple de code suivant :

await NavigationService.NavigateToAsync<MainViewModel>();

L’exemple de code suivant montre les NavigateToAsync méthodes fournies par la NavigationService classe :

public Task NavigateToAsync<TViewModel>() where TViewModel : ViewModelBase  
{  
    return InternalNavigateToAsync(typeof(TViewModel), null);  
}  

public Task NavigateToAsync<TViewModel>(object parameter) where TViewModel : ViewModelBase  
{  
    return InternalNavigateToAsync(typeof(TViewModel), parameter);  
}

Chaque méthode permet à n’importe quelle classe de modèle d’affichage qui dérive de la ViewModelBase classe d’effectuer une navigation hiérarchique en appelant la InternalNavigateToAsync méthode. En outre, la deuxième NavigateToAsync méthode permet aux données de navigation d’être spécifiées en tant qu’argument transmis au modèle d’affichage vers lequel il est généralement utilisé pour effectuer l’initialisation. Pour plus d’informations, consultez Passage de paramètres pendant la navigation.

La InternalNavigateToAsync méthode exécute la demande de navigation et est illustrée dans l’exemple de code suivant :

private async Task InternalNavigateToAsync(Type viewModelType, object parameter)  
{  
    Page page = CreatePage(viewModelType, parameter);  

    if (page is LoginView)  
    {  
        Application.Current.MainPage = new CustomNavigationView(page);  
    }  
    else  
    {  
        var navigationPage = Application.Current.MainPage as CustomNavigationView;  
        if (navigationPage != null)  
        {  
            await navigationPage.PushAsync(page);  
        }  
        else  
        {  
            Application.Current.MainPage = new CustomNavigationView(page);  
        }  
    }  

    await (page.BindingContext as ViewModelBase).InitializeAsync(parameter);  
}  

private Type GetPageTypeForViewModel(Type viewModelType)  
{  
    var viewName = viewModelType.FullName.Replace("Model", string.Empty);  
    var viewModelAssemblyName = viewModelType.GetTypeInfo().Assembly.FullName;  
    var viewAssemblyName = string.Format(  
                CultureInfo.InvariantCulture, "{0}, {1}", viewName, viewModelAssemblyName);  
    var viewType = Type.GetType(viewAssemblyName);  
    return viewType;  
}  

private Page CreatePage(Type viewModelType, object parameter)  
{  
    Type pageType = GetPageTypeForViewModel(viewModelType);  
    if (pageType == null)  
    {  
        throw new Exception($"Cannot locate page type for {viewModelType}");  
    }  

    Page page = Activator.CreateInstance(pageType) as Page;  
    return page;  
}

La InternalNavigateToAsync méthode effectue une navigation vers un modèle d’affichage en appelant d’abord la CreatePage méthode. Cette méthode localise la vue qui correspond au type de modèle d’affichage spécifié et crée et retourne une instance de ce type d’affichage. La localisation de la vue qui correspond au type de modèle d’affichage utilise une approche basée sur une convention, qui suppose que :

  • Les vues se trouvent dans le même assembly que les types de modèle d’affichage.
  • Les vues sont dans un . Affiche l’espace de noms enfant.
  • Les modèles d’affichage sont dans un . Espace de noms enfant ViewModels.
  • Les noms d’affichage correspondent aux noms de modèles d’affichage, avec la suppression de « Modèle ».

Lorsqu’une vue est instanciée, elle est associée à son modèle d’affichage correspondant. Pour plus d’informations sur la façon dont cela se produit, consultez Création automatique d’un modèle d’affichage avec un localisateur de modèle d’affichage.

Si la vue en cours de création est un LoginView, elle est encapsulée à l’intérieur d’une nouvelle instance de la CustomNavigationView classe et affectée à la Application.Current.MainPage propriété. Sinon, l’instance CustomNavigationView est récupérée et fournie qu’elle n’est pas null, la PushAsync méthode est appelée pour envoyer (push) la vue créée sur la pile de navigation. Toutefois, si l’instance récupérée est null, la vue en cours de création est encapsulée CustomNavigationView à l’intérieur d’une nouvelle instance de la CustomNavigationView classe et affectée à la Application.Current.MainPage propriété. Ce mécanisme garantit que pendant la navigation, les pages sont correctement ajoutées à la pile de navigation lorsqu’elles sont vides et lorsqu’elles contiennent des données.

Conseil

Envisagez de mettre en cache des pages. La mise en cache des pages entraîne une consommation de mémoire pour les vues qui ne sont pas affichées actuellement. Toutefois, sans mise en cache de page, cela signifie que l’analyse et la construction XAML de la page et de son modèle d’affichage se produisent chaque fois qu’une nouvelle page est accédée, ce qui peut avoir un impact sur les performances d’une page complexe. Pour une page bien conçue qui n’utilise pas un nombre excessif de contrôles, les performances doivent être suffisantes. Toutefois, la mise en cache des pages peut vous aider si des temps de chargement de page lents sont rencontrés.

Une fois l’affichage créé et accédé, la InitializeAsync méthode du modèle d’affichage associé est exécutée. Pour plus d’informations, consultez Passage de paramètres pendant la navigation.

Lorsque l’application est lancée, la InitNavigation méthode de la App classe est appelée. L’exemple de code suivant illustre cette méthode :

private Task InitNavigation()  
{  
    var navigationService = ViewModelLocator.Resolve<INavigationService>();  
    return navigationService.InitializeAsync();  
}

La méthode crée un NavigationService objet dans le conteneur d’injection de dépendances Autofac et retourne une référence à celui-ci, avant d’appeler sa InitializeAsync méthode.

Remarque

Lorsque l’interface INavigationService est résolue par la ViewModelBase classe, le conteneur retourne une référence à l’objet NavigationService créé lors de l’appel de la méthode InitNavigation.

L’exemple de code suivant montre la NavigationService InitializeAsync méthode :

public Task InitializeAsync()  
{  
    if (string.IsNullOrEmpty(Settings.AuthAccessToken))  
        return NavigateToAsync<LoginViewModel>();  
    else  
        return NavigateToAsync<MainViewModel>();  
}

La MainView valeur est accessible si l’application a un jeton d’accès mis en cache, qui est utilisé pour l’authentification. Sinon, le LoginView chemin est accédé.

Pour plus d’informations sur le conteneur d’injection de dépendances Autofac, consultez Introduction à l’injection de dépendances.

Passage de paramètres pendant la navigation

L’une des NavigateToAsync méthodes, spécifiées par l’interface INavigationService , permet aux données de navigation d’être spécifiées en tant qu’argument passé au modèle d’affichage vers lequel il est généralement utilisé pour effectuer l’initialisation.

Par exemple, la classe ProfileViewModel contient un OrderDetailCommand qui s’exécute quand l’utilisateur sélectionne une commande dans la page ProfileView. En retour, cela entraîne l’exécution de la méthode OrderDetailAsync, comme le montre l’exemple de code suivant :

private async Task OrderDetailAsync(Order order)  
{  
    await NavigationService.NavigateToAsync<OrderDetailViewModel>(order);  
}

Cette méthode appelle la navigation vers l’instance OrderDetailViewModelen passant une Order instance qui représente l’ordre sélectionné par l’utilisateur sur la ProfileView page. Lorsque la classe crée le NavigationService OrderDetailView, la OrderDetailViewModel classe est instanciée et affectée à l’affichage BindingContext. Une fois que vous accédez au modèle d’affichage associé, OrderDetailViewla InternalNavigateToAsync méthode exécute la InitializeAsync méthode du modèle d’affichage associé.

La InitializeAsync méthode est définie dans la ViewModelBase classe en tant que méthode qui peut être substituée. Cette méthode spécifie un object argument qui représente les données à passer à un modèle d’affichage pendant une opération de navigation. Par conséquent, afficher les classes de modèle qui souhaitent recevoir des données d’une opération de navigation fournissent leur propre implémentation de la InitializeAsync méthode pour effectuer l’initialisation requise. L’exemple de code suivant montre la méthode InitializeAsync dans la classe OrderDetailViewModel :

public override async Task InitializeAsync(object navigationData)  
{  
    if (navigationData is Order)  
    {  
        ...  
        Order = await _ordersService.GetOrderAsync(  
                        Convert.ToInt32(order.OrderNumber), authToken);  
        ...  
    }  
}

Cette méthode récupère l’instance Order passée dans le modèle d’affichage pendant l’opération de navigation et l’utilise pour récupérer les détails de commande complets de l’instance OrderService .

Appel de la navigation à l’aide de comportements

La navigation est généralement déclenchée à partir d’une vue par une interaction utilisateur. Par exemple, LoginView effectue la navigation après une authentification réussie. L’exemple de code suivant montre comment la navigation est appelée par un comportement :

<WebView ...>  
    <WebView.Behaviors>  
        <behaviors:EventToCommandBehavior  
            EventName="Navigating"  
            EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"  
            Command="{Binding NavigateCommand}" />  
    </WebView.Behaviors>  
</WebView>

Au moment de l’exécution, le EventToCommandBehavior répond à l’interaction avec le WebView. Lorsque l’utilisateur WebView accède à une page web, l’événement Navigating se déclenche, ce qui exécute le LoginViewModelNavigateCommand fichier . Par défaut, les arguments d’événement de l’événement sont passés à la commande. Ces données sont converties au fur et à mesure qu’elles sont passées entre la source et la cible par le convertisseur spécifié dans la propriété EventArgsConverter, qui retourne Url à partir de WebNavigatingEventArgs. Par conséquent, lorsque l’exécution NavigationCommand est effectuée, l’URL de la page web est passée en tant que paramètre au fichier inscrit Action.

En retour, NavigationCommand exécute la méthode NavigateAsync, comme le montre l’exemple de code suivant :

private async Task NavigateAsync(string url)  
{  
    ...          
    await NavigationService.NavigateToAsync<MainViewModel>();  
    await NavigationService.RemoveLastFromBackStackAsync();  
    ...  
}

Cette méthode appelle la navigation dans la MainViewModelpile de navigation, puis la navigation suivante supprime la LoginView page de la pile de navigation.

Confirmation ou annulation de la navigation

Une application peut être amenée à interagir avec l’utilisateur durant une opération de navigation pour permettre à l’utilisateur de confirmer ou d’annuler la navigation. Cela peut s’avérer nécessaire, par exemple, quand l’utilisateur tente de naviguer avant d’avoir complètement rempli une page d’entrée de données. Dans ce cas, l’application doit fournir une notification qui permet à l’utilisateur de quitter la page, ou d’annuler l’opération de navigation avant qu’elle n’ait lieu. Cela peut être obtenu dans une classe de modèle d’affichage à l’aide de la réponse d’une notification pour contrôler si la navigation est appelée ou non.

Résumé

Xamarin.Forms inclut la prise en charge de la navigation de page, qui résulte généralement de l’interaction de l’utilisateur avec l’interface utilisateur ou de l’application elle-même, en raison des modifications d’état internes pilotées par la logique. Toutefois, la navigation peut être complexe à implémenter dans les applications qui utilisent le modèle MVVM.

Ce chapitre a présenté une NavigationService classe, qui est utilisée pour effectuer une navigation d’affichage du modèle en premier à partir des modèles d’affichage. Placer la logique de navigation dans les classes de modèle d’affichage signifie que la logique peut être exercée via des tests automatisés. En outre, le modèle d’affichage peut ensuite implémenter la logique pour contrôler la navigation afin de s’assurer que certaines règles d’entreprise sont appliquées.