The MVVM Light NavigationService part 2: Xamarin.iOS with Storyboards

Introduction

This series of articles documents how to use the MVVM Light NavigationService on all supported platforms.

In this article, we will see how the NavigationService works in Xamarin.iOS when the user interface is defined with a Storyboard.

See the bottom of this article for a working sample!

Creating the application

In iOS, you can define the UI in multiple ways. Currently the recommended way is to use a so-called Storyboard document, which is an XML file describing all the application’s user interface elements. You can also describe additional information such as the navigation and animations between these pages. In Xamarin, you can edit the Storyboard using a visual designer, either in Visual Studio (Windows only) or in Xamarin Studio (Mac only).

We will start by creating a new application with three pages. You can do the same in Xamarin Studio on Mac, but here we will do it in Visual Studio. Also, note that we will keep things concentrated on the NavigationService only, and we won’t use viewmodels, but trigger the navigation directly from the view.

  • In Visual Studio, select File, New, Project.
  • In the New Project dialog, select Visual C# / iOS / iPhone / Blank App.

Note: you can also do the same with an iPad app or a Universal app.
Note 2: You can also create a Single View app and modify the existing Storyboard, but there is a bug in the current (8.6.0.0) version of Xamarin.iOS which prevents to run a Single View app from Visual Studio on the iOS simulator.

  • Give a name to the new app and click OK.
  • In the Solution Explorer, right click on the newly created project and select Add, New Item.
  • In the Add New Item dialog, select Code and then iPhone Storyboard View Controller.
  • Name the new item MainViewController and click OK.

This creates two new files: MainViewController.cs, which will have the "code-behind" of the main view, i.e. the location where the view is prepared and the events are handled. And MainViewController.storyboard, which is the storyboard document where we will design the UI.

  • Double click MainViewController.storyboard. This will open the visual designer. Note that if you are in Visual Studio on Windows, you will need to be connected to a networked Mac computer with the Xamarin.iOS Build Host running.
  • At this point, you should see the following scene:
2015-02-14_14-38-35
  • Click the black bar saying "View Controller" to reveal the selection button.
  • Click the "Select View Controller" button and then hit Delete. This will delete the main View, because we want to replace it with a new one, managed by a NavigationController.
2015-02-14_14-43-10
  • From the Toolbox, drag a Navigation Controller to the storyboard. If the Toolbox is not visible in your environment, you need to select the menu View, Toolbox.
2015-02-14_14-46-33
  • This will create two new controllers in the storyboard: The Navigation Controller, which is the host for the navigation, and the Root View Controller.
  • Using the same steps as before, select the Root View Controller (click on the black bar, then on the controller selection button)
  • Show the Properties window.
  • Under Identity, open the Class combobox and select the MainViewController class. This creates a logical link between this view and the class that we created earlier.
  • Save everything.
2015-02-14_14-52-10
  • From the toolbox, drag two new View Controllers (they are under Controllers and Objects, at the bottom). Drop these two new controllers on the storyboard surface. These will be our page 2 and 3.
  • Select the "page 2" controller and in the Properties window, in the Class field, type "Page2Controller" and press Enter. This will create a new class in the solution explorer with the same name.
  • In the Properties, enter "Page2Controller" in the Storyboard ID and the Restoration ID fields. This is the ID that we will use to trigger the navigation. A good practice is to use the same name for the Class, Storyboard ID and Restoration ID.
  • Repeat these steps for the "page 3" controller, with the ID "Page3Controller".

At this point, your Solution explorer should look like the picture below. Note that you can use the Properties explorer to set each controller’s properties, notably the Title property (set to "Page 2" and "Page 3"). At this point, the title won’t appear in the visual designer, but it will show up in the navigation bar which is created by the Navigation Controller at runtime. Feel free to set any other property you want.

Creating the UI

Now we will create a very simple UI for the navigation.

  • From the Toolbox, drag and drop a Button on the Root View Controller.
  • With the new Button selected, in the Properties window, set its Name to "GoToPage2Button" and the Title to "Navigate to Page 2".
  • From the Toolbox, drag another Button, this time on the Page2Controller. Name it "GoToPage3Button" and set the title to "Navigate to Page 3 with a parameter".
  • Drag a Button from the Toolbox on the Page3Controller. Name it "GoBackButton" and set its Title to "Go back to Page 2".
  • Finally, drag a Label from the Toolbox on the Page3Controller. Name it "DisplayLabel" and set its Text property to "Nothing yet".
  • Note that you should use constraints to clean up the UI and make sure that everything will show up as expected. If you are not sure how to use constraints on the iOS storyboard, check the Xamarin.com website.

At this point, the Storyboard should look like this:

2015-02-14_15-18-59

The last step is to specify to the application that it should use the Storyboard for the UI.

  • Right click on the project in the Solution Explorer and select Properties.
  • Under iOS Application, set the "Main interface" combobox to "MainViewController.storyboard".

Installing MVVM Light

In order to use MVVM Light, we will pull the latest version from Nuget.

  • Right click on the project in the Solution Explorer, and select Manage NuGet Packages.
  • Select the nuget.org repository (under Online) and enter "mvvmlight" in the Search field.
  • Select the "MVVM Light libraries only" package and click on Install.
  • Follow the instructions.

Initializing and registering the NavigationService

Now we will initialize the navigation service and the IOC container.

  • In the Solution Explorer, open the AppDelegate.
  • Change the "window" field to be a property as shown below:
// class-level declarations
public override UIWindow Window
{
    get;
    set;
}
  • Change the FinishedLaunching method as follows. Note that you don’t need to create a Window manually, because the storyboard will take care of this. Instead, we will start by creating the new NavigationService, and configure it.
    • The first line configures the ServiceLocator, so we can use it in a standard manner throughout the application. For more details on this, see CommonServiceLocator.
    • Calling "Initialize" with the Window.RootViewController as parameter. This instructs the NavigationService to use the built-in navigation system.
    • Calling Configure adds a mapping key –> Storyboard ID in the NavigationService. Note that because we are using a Storyboard, you must make sure to use the Configure(string, string) overload, and NOT the Configure(string, Type) one.
    • Finally, save the configured NavigationService instance in the SimpleIoc.
public const string Page2Key = "Page2";
public const string Page3Key = "Page3";

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    var nav = new NavigationService();
    nav.Initialize((UINavigationController)Window.RootViewController);
    nav.Configure(Page2Key, "Page2Controller");
    nav.Configure(Page3Key, "Page3Controller");
  
    SimpleIoc.Default.Register<INavigationService>(() => nav);
    return true;
}

Navigating to another controller

Now we will trigger the navigation. As stated before, the navigation can be triggered from any location in the application, including shared code in a portable class library, etc. Here, to keep things simple, we will not add any viewmodels but just use the controller code and event handlers. In a real life application, the INavigationService would be injected inside the viewmodel using dependency injection. For more information about dependency injection and IOC, see my MSDN article.

  • Open the MainViewController and locate the ViewDidLoad method.
  • Modify this method as follows:
public override void ViewDidLoad()
{
    base.ViewDidLoad();

    GoToPage2Button.TouchUpInside += (s, e) =>
    {
        var nav = ServiceLocator.Current.GetInstance<INavigationService>();
        nav.NavigateTo(AppDelegate.Page2Key);
    };
}
  • Open the Page2Controller and implement the ViewDidLoad method as follows:
public override void ViewDidLoad()
{
    base.ViewDidLoad();

    GoToPage3Button.TouchUpInside += (s, e) =>
    {
        var nav = ServiceLocator.Current.GetInstance<INavigationService>();
        nav.NavigateTo(
            AppDelegate.Page3Key, 
            "Hello Xamarin " + DateTime.Now.ToString("HH:mm:ss"));
    };
}

Note that in this method, we are calling NavigateTo with a parameter. In this case we pass a string, but it could be any object.

Retrieving the passed parameter, going back

In Page3Controller, we will retrieve the parameter passed by Page2. Then we will display this value in the DisplayLabel. We will also implemement the GoBackButton’s event handler to programmatically go back to page 2. To do this, we will modify the base class of Page3Controller to be a GalaSoft.MvvmLight.Views.ControllerBase. This utility class is useful for this kind of scenario.

partial class Page3Controller : ControllerBase
{
    public Page3Controller (IntPtr handle) : base (handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        GoBackButton.TouchUpInside += (s, e) =>
        {
            var nav = ServiceLocator.Current.GetInstance<INavigationService>();
            nav.GoBack();
        };

        var param = (string)NavigationParameter;
        DisplayLabel.Text = param;
    }
}

Retrieving the CurrentPageKey

It can be useful for the viewmodel to know which page is currently displayed. To do this, you can use the CurrentPageKey property. To illustrate this, implement the following code:

  • Open Page2Controller.
  • Implement the following method:
public override void ViewDidAppear(bool animated)
{
    base.ViewDidAppear(animated);
    Debug.WriteLine(
        "Current page key: "
        + ServiceLocator.Current
            .GetInstance<INavigationService>()
            .CurrentPageKey);
}
  • Run the application in Debug mode (F5).
  • Navigate to Page 2 and observe the Debug console. You should see "Current page key: Page2".
  • Then navigate to Page 3. Using either the Back button in the navigation bar, or the button labeled "Go back to page 2", navigate back.
  • Observe again the Debug console. Using the built-in navigation controller’s Back button, or our own back button should not make any difference.

Conclusion

This article demonstrated how to create a new iOS Storyboard-based application with 3 pages, install MVVM Light and implement navigation between the pages, with and without a parameter. We also showed how to retrieve the parameter, and how to navigate back programmatically. Finally, we showed how to retrieve the key corresponding to the current page.

Resources

The code shown in this article is available in a sample.