Share via


Navigation des applications d’entreprise

Notes

Ce livre électronique a été publié au printemps 2017 et n’a pas été mis à jour depuis lors. Il y a beaucoup dans le livre qui reste précieux, mais une partie du matériel est obsolète.

Xamarin.Forms inclut la prise en charge de la navigation sur 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 suite à des changements d’état internes pilotés par la logique. 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 l’affichage vers lequel naviguer, à l’aide d’une approche qui n’introduit pas de couplage étroit et de dépendances entre les vues.
  • Comment coordonner le processus par lequel la vue à parcourir est instanciée et initialisée. Lorsque vous utilisez MVVM, le modèle d’affichage et le modèle d’affichage doivent être instanciés et associés 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 d’affichage peut nécessiter un mécanisme de construction spécifique.
  • Indique s’il faut effectuer une navigation d’affichage d’abord ou afficher la navigation en premier 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 afficher d’abord le modèle, où la page à parcourir fait référence au nom du type de modèle d’affichage.
  • Comment séparer proprement 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 couvre souvent les parties de l’interface utilisateur et des 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 doit souvent être lancé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, pour s’assurer 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 résout ces problèmes en présentant une NavigationService classe qui est utilisée pour effectuer la navigation afficher la première page du modèle.

Notes

Le NavigationService utilisé par l’application est conçu 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ée aux données. Bien que le placement de la logique de navigation dans une vue puisse être l’approche la plus simple, elle n’est pas facilement testable par le biais de tests unitaires. Le fait de placer la logique de navigation dans des classes de modèle d’affichage signifie que la logique peut être exécutée par le biais de tests unitaires. En outre, le modèle d’affichage peut ensuite implémenter une 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 des vues auxquelles le modèle d’affichage actif n’est pas associé, ce qui n’est pas recommandé. Par conséquent, le NavigationService présenté ici spécifie le type de modèle d’affichage comme cible à atteindre.

L’application mobile eShopOnContainers utilise la NavigationService classe pour fournir la navigation afficher d’abord le modèle. 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é renvoie le type de modèle d’affichage associé à la page précédente dans la pile de navigation.

Notes

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

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 constructeur de ViewModelBase classe, comme illustré dans l’exemple de code suivant :

NavigationService = ViewModelLocator.Resolve<INavigationService>();

Cette opération retourne 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 dans 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 de vue.

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 naviguer dans les pages, en avant et en arrière, selon ses besoins. 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 cet habillage est de faciliter le NavigationPage style des instance à l’intérieur du fichier XAML pour la classe .

La navigation s’effectue à l’intérieur des classes de modèle d’affichage en appelant l’une NavigateToAsync des méthodes, en spécifiant le type de modèle de vue pour la page vers laquelle vous accédez, 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 à toute classe de modèle d’affichage dérivée de la classe d’effectuer une ViewModelBase navigation hiérarchique en appelant la InternalNavigateToAsync méthode . En outre, la deuxième NavigateToAsync méthode permet de spécifier les données de navigation en tant qu’argument transmis au modèle de vue vers lequel vous accédez, où 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 la 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é, puis 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èles d’affichage.
  • Les vues se trouvent dans un . Affiche l’espace de noms enfant.
  • Les modèles d’affichage se trouvent dans un . Espace de noms enfant ViewModels.
  • Les noms d’affichage correspondent aux noms de modèles d’affichage, avec « Modèle » supprimé.

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 de vue.

Si la vue en cours de création est , LoginViewelle est encapsulée dans une nouvelle instance de la CustomNavigationView classe et affectée à la Application.Current.MainPage propriété . Sinon, la CustomNavigationView instance est récupérée et, à condition qu’elle n’ait pas la valeur Null, la PushAsync méthode est appelée pour envoyer (push) la vue en cours de création vers la pile de navigation. Toutefois, si le instance récupéré CustomNavigationView est null, la vue en cours de création est encapsulée dans 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’elle est vide et lorsqu’elle contient des données.

Conseil

Envisagez de mettre en cache les pages. La mise en cache des pages entraîne la consommation de mémoire pour les affichages qui ne sont pas affichés 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 que vous accédez à une nouvelle page, 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 être utile si des temps de chargement de page lents sont rencontrés.

Une fois la vue créée et parcourue, la InitializeAsync méthode du modèle d’affichage associé à l’affichage 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 dans 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.

Notes

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 NavigationServiceInitializeAsync méthode :

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

Le MainView est accédé à si l’application a un jeton d’accès mis en cache, qui est utilisé pour l’authentification. Dans le cas contraire, le LoginView est accédé à.

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

Passage de paramètres pendant la navigation

L’une des NavigateToAsync méthodes, spécifiée par l’interface INavigationService , permet de spécifier les données de navigation en tant qu’argument transmis au modèle de vue vers lequel vous accédez, où 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 , OrderDetailViewModelen passant un Order instance qui représente l’ordre que l’utilisateur a sélectionné sur la ProfileView page. Lorsque la NavigationService classe crée le OrderDetailView, la OrderDetailViewModel classe est instanciée et affectée au BindingContext. Après avoir accédé à , OrderDetailViewla InternalNavigateToAsync méthode exécute la InitializeAsync méthode du modèle d’affichage associé à la vue.

La InitializeAsync méthode est définie dans la ViewModelBase classe en tant que méthode qui peut être remplacée. Cette méthode spécifie un object argument qui représente les données à passer à un modèle d’affichage au cours d’une opération de navigation. Par conséquent, les classes de modèle d’affichage 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 le Order instance passé dans le modèle d’affichage pendant l’opération de navigation et l’utilise pour récupérer les détails complets de la commande à partir 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 le WebView accède à une page web, l’événement Navigating se déclenche, ce qui exécute dans NavigateCommand le LoginViewModel. 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 le NavigationCommand est exécuté, l’URL de la page web est passée en tant que paramètre à l’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 vers le MainViewModel, et 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. Vous pouvez y parvenir dans une classe de modèle d’affichage en utilisant 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 sur 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, suite à des changements d’état internes pilotés 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 la navigation afficher d’abord le modèle à partir de modèles d’affichage. Le fait de placer la logique de navigation dans des classes de modèle d’affichage signifie que la logique peut être exécutée par le biais de tests automatisés. En outre, le modèle d’affichage peut ensuite implémenter une logique pour contrôler la navigation afin de s’assurer que certaines règles d’entreprise sont appliquées.