Windows 8 : Utiliser la Navigation avec MVVM

Lorsque l’on a l’habitude de créer des projets avec le pattern MVVM, la première chose que l’on souhaite faire sur tout nouveau projet , c’est de mettre en place les concepts que l’on a déjà mis en œuvre lors des différents projets auxquels nous aurions pu participé.

Dans le cadre d’un nouveau projet Windows 8, nous nous posons la question qui est “Comment utiliser la navigation à partir de nos ViewModels?” Nous allons vous montrer comment intégrer un service de navigation qui pourra être utilisé au travers de nos ViewModels.

Définition de l’interface INavigationService

Avant de pouvoir naviguer au travers de nos ViewModels, nous avons besoin d’en définir le squelette :

Code Snippet

  1. public interface INavigationService
  2.     {
  3.         object Parameter { get; }
  4.         event NavigatingCancelEventHandler Navigating;
  5.         
  6.         void NavigateTo(Type page, object parameter = null);
  7.         void GoBack();
  8.         void Initialized(Frame frame);
  9.     }

L’interface étant créée, nous allons détailler l’utilité de chacune des signatures de méthodes :

  • void NavigateTo(Type page, object parameter=null)
    Cette méthode va nous permettre naviguer vers une autre page avec ou sans paramètre.
  • void GoBack() 
    Cette méthode appellera la méthode native GoBack de l’objet Frame pour revenir à la page précédemment vue
  • void Initialized(Frame frame)
    Point d’entrée de la navigation qui va définir les vues que vous voulez enregistrer.
    L’object Frame est  celui qui est définit dans le fichier App.xaml.cs. Cet objet est le point d’entrée de votre application  
  • object Parameter { get; }
    Cet attribut jouera un rôle particulier car c’est au travers de celui-ci que nous pourrons récupérer le paramètre passer d’un ViewModel à un autre.
     

Création de la classe de Navigation 

Une fois l’interface créée nous pouvons donc l’implémenter comme suit:

Code Snippet

  1. public class NavigationService : INavigationService
  2.     {
  3.  
  4.         public event NavigatingCancelEventHandler Navigating;
  5.  
  6.         private Frame frame;
  7.         public object Parameter
  8.         {
  9.             get;
  10.             private set;
  11.         }
  12.  
  13.         public void NavigateTo(Type page, object parameter = null)
  14.         {
  15.             if (null != parameter)
  16.             {
  17.                 Parameter = parameter;
  18.                 frame.Navigate(page, parameter);
  19.             }
  20.             else
  21.             {
  22.                 Parameter = null;
  23.                 frame.Navigate(page);
  24.             }
  25.         }
  26.  
  27.         public void GoBack()
  28.         {
  29.             if (EnsureMainFrame() && frame.CanGoBack)
  30.             {
  31.                 frame.GoBack();
  32.             }
  33.         }
  34.  
  35.         public void Initialized(Frame frame)
  36.         {
  37.             if (null == frame)
  38.             {
  39.                 throw new ArgumentNullException();
  40.             }
  41.  
  42.             if (null != frame)
  43.             {
  44.                 frame.Navigating -= FrameNavigating;
  45.             }
  46.             this.frame = frame;
  47.             this.frame.Navigating += FrameNavigating;
  48.         }
  49.  
  50.         private bool EnsureMainFrame()
  51.         {
  52.             return null != frame;
  53.         }
  54.  
  55.         private void FrameNavigating(object sender, NavigatingCancelEventArgs e)
  56.         {
  57.             if (null != Navigating)
  58.                 Navigating(this, e);
  59.         }
  60.     }

 

Comment utiliser cette nouvelle classe

La mise en œuvre de cette classe se fait en trois étapes qui sont les suivantes :

1- Créer une classe intermédiaire n’autorisant l’accès qu’à une seule instance de la navigation et lui permettant par la même occasion d’enregistrer les vues créées dans votre application

Code Snippet

  1. public class ViewService
  2. {
  3.     private IDictionary<string, Type> _viewRegistered;
  4.  
  5.     private static ViewService _instance;
  6.     public static ViewService Service
  7.     {
  8.         get
  9.         {
  10.             return _instance ?? (_instance = new ViewService());
  11.         }
  12.     }
  13.  
  14.     private ViewService()
  15.     {
  16.         _viewRegistered = new Dictionary<string, Type>();
  17.     }
  18.  
  19.     private INavigationService _navigationService;
  20.  
  21.     public INavigationService NavigationService
  22.     {
  23.         get
  24.         {
  25.             return _navigationService ?? (_navigationService = new NavigationService());
  26.         }
  27.     }
  28.  
  29.     public void RegisterView(string navigationStringValue, Type typeOfView)
  30.     {
  31.         if( !_viewRegistered.ContainsKey(navigationStringValue))
  32.              _viewRegistered.Add(new KeyValuePair<string,Type>(navigationStringValue, typeOfView));
  33.     }
  34.  
  35.     public Type GetView(string navigationStringValue)
  36.     {
  37.         if (string.IsNullOrWhiteSpace(navigationStringValue))
  38.         {
  39.             throw new ArgumentNullException();
  40.         }
  41.  
  42.         if (!_viewRegistered.Any())
  43.         {
  44.             return null;
  45.         }
  46.  
  47.         if (!_viewRegistered.ContainsKey(navigationStringValue))
  48.         {
  49.             throw new ArgumentOutOfRangeException();
  50.         }
  51.  
  52.         return _viewRegistered[navigationStringValue];
  53.     }
  54. }

2- Renseigner les vues de votre application au travers de la précédente classe créée

Cette initialisation s’effectue pour le cadre de cette démo dans le le fichier du App.xaml.cs

Code Snippet

  1. private void InitializeViewService(Frame rootFrame)
  2.         {
  3.             ViewService.Service.NavigationService.Initialized(rootFrame);
  4.             ViewService.Service.RegisterView("BlankPage1", typeof(BlankPage1));
  5.             ViewService.Service.RegisterView("BlankPage2", typeof(BlankPage2));
  6.         }

3- La navigation à partir des ViewModels

A partir de cette étape, il ne vous reste plus qu’à utiliser la méthode NavigateTo de la classe NavigationService pour naviguer d’une page à une autre. Comme vous pouvez le voir ci-dessous, une seule ligne de code nous permet de gérer la navigation d’une vue courante à une autre.

Code Snippet

  1. public ICommand GoToMyPage1Command { get; private set; }
  2.         
  3.         private void InitializeCommands()
  4.         {
  5.             this.GoToMyPage1Command = CommandFactory.CreateCommand(GoToMyPage1, () => { return true; });
  6.         }
  7.  
  8.         private void GoToMyPage1()
  9.         {
  10.             ViewService.Service.NavigationService.NavigateTo(
  11.                 ViewService.Service.GetView("Page1"));
  12.         }

L’exemple ci-dessus montre l’utilisation du service sans passer de paramètre. Dans le cas où nous avons besoin de passer une quelconque information à un autre ViewModel  grâce à un paramètre, il nous faut alors utiliser la navigation de la manière suivante :

Code Snippet

  1. public ICommand GoToMyPage1Command { get; private set; }
  2.         
  3.         private void InitializeCommands()
  4.         {
  5.             this.GoToMyPage1Command = CommandFactory.CreateCommand<MyClass>(GoToMyPage1, (o) => { return true; });
  6.         }
  7.  
  8.         private void GoToMyPage1(MyClass parameter)
  9.         {
  10.             ViewService.Service.NavigationService.NavigateTo(
  11.                 ViewService.Service.GetView("Page1"), parameter);
  12.         }

 

Code source

Le code source de la démo se trouve ICI.

- Patrick Payet -