The MVVM Light NavigationService part 3: Xamarin.iOS without 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 without Storyboards. In this article, we will define the UI in code, but you an also use the Interface Builder to create XIB files.

See the bottom of this article for a working sample!

Creating the application

In iOS, you can define the UI in multiple ways. The "classic" way (which is not the recommended way anymore) is to create the Controllers in code and to define the UI either in code, or in a XIB file. Note that you can also mix the approaches, and have code-only controllers in an application using a storyboard too.

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.

  • 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 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 MainController.xib, which is the interface builder file in which you can define the UI. In this article, we won't use this file, and we will define the UI in code instead. You can either keep the XIB file or delete it (but if you delete it, you must also delete the MainViewController constructor in the code!)

  • Add two new iPhone View Controllers to the project. Name them Page2Controller and Page3Controller. These will be our page 2 and 3.

At this point, your Solution explorer should look like the picture below.

2015-02-15_14-54-05

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 Navigation Controller and NavigationService

Now we will create the UINavigationController that will handle the navigation between the pages. We will also initialize the navigation service and the IOC container.

  • In the Solution Explorer, open the AppDelegate.
  • Change the FinishedLaunching method as follows.
    • First we create the application’s Navigation Controller, starting with the MainViewController. We assign this Navigation Controller to the window’s RootViewController.
    • Then we configure 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 –> Type in the NavigationService. Note that because we are not using a Storyboard, you must make sure to use the Configure(string, Type) overload, and NOT the Configure(string, string) 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)
{
    // create a new window instance based on the screen size
    window = new UIWindow(UIScreen.MainScreen.Bounds);

    // Add the Navigation Controller and initialize it
    var navController = new UINavigationController(new MainViewController());
    window.RootViewController = navController;

    // Initialize and register the Navigation Service
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    var nav = new NavigationService();
    nav.Initialize(navController);
    nav.Configure(Page2Key, typeof(Page2Controller));
    nav.Configure(Page3Key, typeof(Page3Controller));

    SimpleIoc.Default.Register<INavigationService>(() => nav);

    // make the window visible
    window.MakeKeyAndVisible();
    return true;
}

Creating the UI, navigating

Now we will create a simple user interface and use it to 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();

    Title = "Main View";
    View.BackgroundColor = UIColor.White;

    var button = UIButton.FromType(UIButtonType.RoundedRect);
    button.SetTitle("Go to page 2", UIControlState.Normal);
    button.Frame = new CoreGraphics.CGRect(20, 80, 200, 50);
    View.Add(button);

    button.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();

    Title = "Page 2";
    View.BackgroundColor = UIColor.White;

    var button = UIButton.FromType(UIButtonType.RoundedRect);
    button.SetTitle("Go to page 3 with parameter", UIControlState.Normal);
    button.Frame = new CoreGraphics.CGRect(20, 80, 200, 50);
    View.Add(button);

    button.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 a label. We will also add a button to programatically go back to page 2.

First we define a field to save the passed parameter, which will be passed by the NavigationService to the Controller’s constructor when it is created. We add a new constructor accepting a string as parameter. This is the constructor that will be used by the NavigationService to instantiate the controller.

In ViewDidLoad, we proceed as before in MainViewController and Page2Controller. Note how we assigne the passed parameter’s value to the label, for display.

public partial class Page3Controller : UIViewController
{
    private readonly string _passedParameter;

    public Page3Controller(string parameter)
        : base("Page3Controller", null)
    {
        _passedParameter = parameter;
    }

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

        Title = "Page 3";
        View.BackgroundColor = UIColor.White;

        var button = UIButton.FromType(UIButtonType.RoundedRect);
        button.SetTitle("Go back to page 2", UIControlState.Normal);
        button.Frame = new CoreGraphics.CGRect(20, 80, 200, 50);
        View.Add(button);

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

        var label = new UILabel(new CoreGraphics.CGRect(20, 150, 200, 100));
        View.Add(label);
        label.Text = _passedParameter;
    }
}

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 XIB-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.