4
votes

One of the many benefits of implementing any pattern is to have a separation of concerns between the different layers in an application. In the case of Silverlight and MVVM it is my opinion that the NavigationService belongs to the UI.

If the NavigationService belongs to the UI then it should be used in the XAML code behind, but the commands happens on the ViewModel. Should I raise an event on the Command in the ViewModel and let the View handle the event and call the Navigation? That sounds a little absurd if all I'm doing is simply navigating to another page. Shouldn't I just handle the UI event directly and navigate from there?

View Control Event -> ViewModel Command -> Raise Event -> View Handled Event -> Navigation

or

View Control Event -> View Handled Event -> Navigation

2
Actually it is your Choice how you implement the pattern and how you solve problems like this. Maybe the best practice would be to use a Messaging like MVVM Ligh Messenger and so send a message from your ViewModel that the View Should navigate, and have a resource class or something that navigates between your Views. :)BigL

2 Answers

6
votes

There are two documented approaches to this problem

  1. Implementing the navigation using MVVM Light's messaging functionality

    This approach was put forward by Jesse Liberty in Part 3 his MVVM Ligtht soup to nuts series. His approach is to send a message from the command to the view indicating that a navigation operation should take place.

  2. Implementing a ViewService that handles the navigation

    This approach was Laurent Bugnion's response to Jesse's post. This implements a service that handles all navigation operations triggered by the view models.

Both approaches deal only with navigation in WP7 applications. However, they can be adapted to Silverligt applications too.

Jesse's approach is easier to use in SL as it does not require access to the root visual. However, the navigation code gets distributed in several places and requires code behind to do the actual navigation.

Laurent's approach requires access to the root visual - which is used for accessing the built-in navigation functionality. Getting access to this, as shown in Laurent's code, is no big deal in WP7 applications. In SL applications, however, it is slightly more complicated as there is no sourrounding frame. However, I alreay implented the pattern for SL in one of my projects using an attached property do do the required wiring - so although requires more work, it is usable for SL as well.

So concluding - although, Jesse's approach is easier to implement, personally I prefer Laurent's approach for it is cleaner architecture - there is no code behind required, and the functioality is encapsulated into a separate component and thus located at a single point.

0
votes

A bit late to this question, but it is relevant and will hopefully be of benefit to someone. I had to create a SL4 application with MvvmLight and wanted to use a navigation service wrapper that was mock-able and could be injected into the ViewModel. I found a good starting point here: Laurent Bugnion's SL4 sample code samples from Mix11 which includes a navigation service demo: Deep Dive MVVM Mix11

Here are the essential parts for implementing a mock-able navigation service that can be used with Silverlight 4. The key issue is getting a reference to the main navigation frame to be used in the custom NavigationService class.

1) In MainPage.xaml, the navigation frame is given a unique name, for this example it will be ContentFrame:

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}"
    Source="/Home" Navigated="ContentFrame_Navigated"  
    NavigationFailed="ContentFrame_NavigationFailed">
    <!-- UriMappers here -->
</navigation:Frame>

2) In MainPage.xaml.cs, the navigation frame is exposed as a property:

public Frame NavigationFrame
{
    get { return ContentFrame; }
}

3) The navigation service class implements the INavigationService interface and relies on the NavigationFrame property of MainPage.xaml.cs to get a reference to the navigation frame:

public interface INavigationService
{
    event NavigatingCancelEventHandler Navigating;
    void NavigateTo(Uri uri);
    void GoBack();
}

public class NavigationService : INavigationService
{
    private Frame _mainFrame;
    public event NavigatingCancelEventHandler Navigating;
    public void NavigateTo(Uri pageUri)
    {
        if (EnsureMainFrame())
            _mainFrame.Navigate(pageUri);
    }
    public void GoBack()
    {
        if (EnsureMainFrame() && _mainFrame.CanGoBack)
            _mainFrame.GoBack();
    }
    private bool EnsureMainFrame()
    {
        if (_mainFrame != null)
            return true;
        var mainPage = (Application.Current.RootVisual as MainPage);
        if (mainPage != null)
        {
            // **** Here is the reference to the navigation frame exposed earlier in steps 1,2 
            _mainFrame = mainPage.NavigationFrame;
            if (_mainFrame != null)
            {
                // Could be null if the app runs inside a design tool
                _mainFrame.Navigating += (s, e) =>
                {
                    if (Navigating != null)
                    {
                        Navigating(s, e);
                    }
                };
                return true;
            }
        }
        return false;
    }
}