windows phone:Navigation in a #WP7 application with MVVM Light

转载于 http://geekswithblogs.net/lbugnion/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx

In MVVM applications, it can be a bit of a challenge to send instructions to the view (for example a page) from a viewmodel. Thankfully, we have good tools at our disposal to help with that.

In his excellent series “MVVM Light Toolkit soup to nuts”, Jesse Liberty proposes one approach using the MVVM Light messaging infrastructure. While this works fine, I would like to show here another approach using what I call a “view service”, i.e. an abstracted service that is invoked from the viewmodel, and implemented on the view.

Multiple kinds of view services

In fact, I use view services quite often, and even started standardizing them for the Windows Phone 7 applications I work on. If there is interest, I will be happy to show other such view services, for example

  • Animation services, responsible to start/stop animations on the view.
  • Dialog service, in charge of displaying messages to the user and gathering feedback.
  • Navigation service, in charge of navigating to a given page directly from the viewmodel.

In this article, I will concentrate on the navigation service.

The INavigationService interface

In most WP7 apps, the navigation service is used in quite a straightforward way. We want to:

  • Navigate to a given URI.
  • Go back.
  • Be notified when a navigation is taking place, and be able to cancel.

The INavigationService interface is quite simple indeed:

1. public interface INavigationService
2. {
3.     event NavigatingCancelEventHandler Navigating;
4.     void NavigateTo(Uri pageUri);
5.     void GoBack();
6. }

Obviously, this interface can be extended if necessary, but in most of the apps I worked on, I found that this covers my needs.

The NavigationService class

It is possible to nicely pack the navigation service into its own class. To do this, we need to remember that all the PhoneApplicationPage instances use the same instance of the navigation service, exposed through their NavigationService property. In fact, in a WP7 application, it is the main frame (RootFrame, of type PhoneApplicationFrame) that is responsible for this task. So, our implementation of the NavigationService class can leverage this.

First the class will grab the PhoneApplicationFrame and store a reference to it. Also, it registers a handler for the Navigating event, and forwards the event to the listening viewmodels (if any).

Then, the NavigateTo and the GoBack methods are implemented. They are quite simple, because they are in fact just a gateway to the PhoneApplicationFrame.

The whole class is as follows:

01. public class NavigationService : INavigationService
02. {
03.     private PhoneApplicationFrame _mainFrame;
04.  
05.     public event NavigatingCancelEventHandler Navigating;
06.  
07.     public void NavigateTo(Uri pageUri)
08.     {
09.         if (EnsureMainFrame())
10.         {
11.             _mainFrame.Navigate(pageUri);
12.         }
13.     }
14.  
15.     public void GoBack()
16.     {
17.         if (EnsureMainFrame()
18.             && _mainFrame.CanGoBack)
19.         {
20.             _mainFrame.GoBack();
21.         }
22.     }
23.  
24.     private bool EnsureMainFrame()
25.     {
26.         if (_mainFrame != null)
27.         {
28.             return true;
29.         }
30.  
31.         _mainFrame = Application.Current.RootVisual as PhoneApplicationFrame;
32.  
33.         if (_mainFrame != null)
34.         {
35.             // Could be null if the app runs inside a design tool
36.             _mainFrame.Navigating += (s, e) =>
37.             {
38.                 if (Navigating != null)
39.                 {
40.                     Navigating(s, e);
41.                 }
42.             };
43.  
44.             return true;
45.         }
46.  
47.         return false;
48.     }
49. }

Exposing URIs

I find that it is a good practice to expose each page’s URI as a constant. In MVVM Light applications, a good place to do that is the ViewModelLocator, which already acts like a central point of setup for the views and their viewmodels.

Note that in some cases, it is necessary to expose the URL as a string, for instance when a query string needs to be passed to the view. So for example we could have:

1. public static readonly Uri MainPageUri = new Uri("/MainPage.xaml", UriKind.Relative);
2. public const string AnotherPageUrl = "/AnotherPage.xaml?param1={0}&param2={1}";

Creating and using the NavigationService

Normally, we only need one instance of the NavigationService class. In cases where you use an IOC container, it is easy to simply register a singleton instance. For example, I am using a modified version of a super simple IOC container, and so I can register the navigation service as follows:

1. SimpleIoc.Register<INavigationService, NavigationService>();

Then, it can be resolved where needed with:

1. SimpleIoc.Resolve<INavigationService>();

Or (more frequently), I simply declare a parameter on the viewmodel constructor of type INavigationService and let the IOC container do its magic and inject the instance of the NavigationService when the viewmodel is created.

On supported platforms (for example Silverlight 4), it is also possible to use MEF. Or, of course, we can simply instantiate the NavigationService in the ViewModelLocator, and pass this instance as a parameter of the viewmodels’ constructor, injected as a property, etc…

Once the instance has been passed to the viewmodel, it can be used, for example with:

1. NavigationService.NavigateTo(ViewModelLocator.ComparisonPageUri);

Testing

Thanks to the INavigationService interface, navigation can be mocked and tested when the viewmodel is put under unit test. Simply implement and inject a mock class, and assert that the methods are called as they should by the viewmodel.

Conclusion

As usual, there are multiple ways to code a solution answering your needs. I find that view services are a really neat way to delegate view-specific responsibilities such as animation, dialogs and of course navigation to other classes through an abstracted interface. In some cases, such as the NavigationService class exposed here, it is even possible to standardize the implementation and pack it in a class library for reuse. I hope that this sample is useful!

Happy coding.

Laurent

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值