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.
Navigation entre les pages
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 , LoginView
elle 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.
Navigation lors du lancement de l’application
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 NavigationService
InitializeAsync
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 , OrderDetailViewModel
en 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é à , OrderDetailView
la 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.