Chapter 7. Sharing with MvvmCross

In the previous chapter, we covered the basic approaches to reusing code across projects and platforms. In this chapter, we will take the next step and look at how the use of design patterns and frameworks can increase the amount of code that can be reused. We will cover the following topics:

  • An introduction to MvvmCross
  • The MVVM design pattern
  • Core concepts
  • Views, ViewModels, and commands
  • Data binding
  • Navigation (ViewModel to ViewModel)
  • The project organization
  • The startup process
  • Creating NationalParks.MvvmCross

It's more than a little ambitious to try to cover MvvmCross along with a working example in a single chapter. Our approach will be to introduce the core concepts at a high level and then dive in and create the national parks sample app using MvvmCross. This will give you a basic understanding of how to use the framework and the value associated with its use. With that in mind, let's get started.

Introducing MvvmCross

MvvmCross is an open source framework that was created by Stuart Lodge. It is based on the Model-View-ViewModel (MVVM) design pattern and is designed to enhance code reuse across numerous platforms, including Xamarin.Android, Xamarin.iOS, Windows Phone, Windows Store, WPF, and Mac OS X. The MvvmCross project is hosted on GitHub and can be accessed at https://github.com/MvvmCross/MvvmCross.

The MVVM pattern

MVVM is a variation of the Model-View-Controller pattern. It separates logic traditionally placed in a View object into two distinct objects, one called View and the other called ViewModel. The View is responsible for providing the user interface and the ViewModel is responsible for the presentation logic. The presentation logic includes transforming data from the Model into a form that is suitable for the user interface to work with and mapping user interaction with the View into requests sent back to the Model. The following diagram depicts how the various objects in MVVM communicate:

The MVVM pattern

While MVVM presents a more complex implementation model, there are significant benefits of it, which are as follows:

  • ViewModels and their interactions with Models can generally be tested using frameworks (such as NUnit) that are much easier than applications that combine the user interface and presentation layers
  • ViewModels can generally be reused across different user interface technologies and platforms

These factors make the MVVM approach both flexible and powerful.

Views

Views in an MvvmCross app are implemented using platform-specific constructs. For iOS apps, Views are generally implemented as ViewControllers and XIB files. MvvmCross provides a set of base classes, such as MvxViewContoller, that iOS ViewControllers inherit from. Storyboards can also be used in conjunction with a custom presenter to create Views; we will briefly discuss this option in the section titled Implementing the iOS user interface later in this chapter.

For Android apps, Views are generally implemented as MvxActivity or MvxFragment along with their associated layout files.

ViewModels

ViewModels are classes that provide data and presentation logic to views in an app. Data is exposed to a View as properties on a ViewModel, and logic that can be invoked from a View is exposed as commands. ViewModels inherit from the MvxViewModel base class.

Commands

Commands are used in ViewModels to expose logic that can be invoked from the View in response to user interactions. The command architecture is based on the ICommand interface used in a number of Microsoft frameworks such as Windows Presentation Foundation (WPF) and Silverlight. MvvmCross provides IMvxCommand, which is an extension of ICommand, along with an implementation named MvxCommand.

The commands are generally defined as properties on a ViewModel. For example:

public  IMvxCommand ParkSelected { get; protected set; }

Each command has an action method defined, which implements the logic to be invoked:

protected void ParkSelectedExec(NationalPark park)
{
   . . .// logic goes here
}

The commands must be initialized and the corresponding action method should be assigned:

ParkSelected =
    new MvxCommand<NationalPark> (ParkSelectedExec);

Data binding

Data binding facilitates communication between the View and the ViewModel by establishing a two-way link that allows data to be exchanged. The data binding capabilities provided by MvvmCross are based on capabilities found in a number of Microsoft XAML-based UI frameworks such as WPF and Silverlight. The basic idea is that you would like to bind a property in a UI control, such as the Text property of an EditText control in an Android app to a property of a data object such as the Description property of NationalPark. The following diagram depicts this scenario:

Data binding

The binding modes

There are four different binding modes that can be used for data binding:

  • OneWay binding: This mode tells the data binding framework to transfer values from the ViewModel to the View and transfer any updates to properties on the ViewModel to their bound View property.
  • OneWayToSource binding: This mode tells the data binding framework to transfer values from the View to the ViewModel and transfer updates to View properties to their bound ViewModel property.
  • TwoWay binding: This mode tells the data binding framework to transfer values in both directions between the ViewModel and View, and updates on either object will cause the other to be updated. This binding mode is useful when values are being edited.
  • OneTime binding: This mode tells the data binding framework to transfer values from ViewModel to View when the binding is established; in this mode, updates to ViewModel properties are not monitored by the View.

The INotifyPropertyChanged interface

The INotifyPropertyChanged interface is an integral part of making data binding work effectively; it acts as a contract between the source object and the target object. As the name implies, it defines a contract that allows the source object to notify the target object when data has changed, thus allowing the target to take any necessary actions such as refreshing its display.

The interface consists of a single event—the PropertyChanged event—that the target object can subscribe to and that is triggered by the source if a property changes. The following sample demonstrates how to implement INotifyPropertyChanged:

public class NationalPark : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler
     PropertyChanged;
  // rather than use "… code" it is safer to use
  // the comment form 
  string _name;
  public string Name
  {
    get { return _name; }
    set
    {
        if (value.Equals (_name,
            StringComparison.Ordinal))
        {
      // Nothing to do - the value hasn't changed;
      return;
        }
        _name = value;
        OnPropertyChanged();
    }
  }
  . . . 
  void OnPropertyChanged(
    [CallerMemberName] string propertyName = null)
  {
      var handler = PropertyChanged;
  if (handler != null)
  {
      handler(this,
            new PropertyChangedEventArgs(propertyName));
  }
  }
}

Binding specifications

Bindings can be specified in a couple of ways. For Android apps, bindings can be specified in layout files. The following example demonstrates how to bind the Text property of a TextView instance to the Description property in a NationalPark instance:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/descrTextView"
    local:MvxBind="Text Park.Description" />

For iOS, binding must be accomplished using the binding API. CreateBinding() is a method than can be found on MvxViewController. The following example demonstrates how to bind the Description property to a UILabel instance:

this.CreateBinding (this.descriptionLabel).
    To ((DetailViewModel vm) => vm.Park.Description).
    Apply ();

Navigating between ViewModels

Navigating between various screens within an app is an important capability. Within a MvvmCross app, this is implemented at the ViewModel level so that navigation logic can be reused. MvvmCross supports navigation between ViewModels through use of the ShowViewModel<T>() method inherited from MvxNavigatingObject, which is the base class for MvxViewModel. The following example demonstrates how to navigate to DetailViewModel:

ShowViewModel<DetailViewModel>();

Passing parameters

In many situations, there is a need to pass information to the destination ViewModel. MvvmCross provides a number of ways to accomplish this. The primary method is to create a class that contains simple public properties and passes an instance of the class into ShowViewModel<T>(). The following example demonstrates how to define and use a parameters class during navigation:

public class DetailParams
{
    public int ParkId { get; set; }
}

// using the parameters class
ShowViewModel<DetailViewModel>(
new DetailViewParam() { ParkId = 0 });

To receive and use parameters, the destination ViewModel implements an Init() method that accepts an instance of the parameters class:

public class DetailViewModel : MvxViewModel
{
    . . .
    public void Init(DetailViewParams parameters)
    {
        // use the parameters here . . .
    }
}

Solution/project organization

MvvmCross solutions are organized in a way that is similar to how we organized the PCL solution in Chapter 6, The Sharing Game. Each MvvmCross solution will have a single core PCL project that houses the reusable code and a series of platform-specific projects that contain the various apps. The following diagram depicts the general structure:

Solution/project organization

The startup process

MvvmCross apps generally follow a standard startup sequence that is initiated by platform-specific code within each app. There are several classes that collaborate to accomplish the startup; some of these classes reside in the core project and some of them reside in the platform-specific projects. The following sections describe the responsibilities of each of the classes involved.

App.cs

The core project has an App class that inherits from MvxApplication. The App class contains an override to the Initialize() method so that at a minimum, it can register the first ViewModel that should be presented when the app starts:

RegisterAppStart<ViewModels.MasterViewModel>();

Setup.cs

Android and iOS projects have a Setup class that is responsible for creating the App object from the core project during the startup. This is accomplished by overriding the CreateApp() method:

protected override IMvxApplication CreateApp()
{
    return new Core.App();
}

For Android apps, Setup inherits from MvxAndroidSetup. For iOS apps, Setup inherits from MvxTouchSetup.

The Android startup

Android apps are kicked off using a special Activity splash screen that calls the Setup class and initiates the MvvmCross startup process. This is all done automatically for you; all you need to do is include the splash screen definition and make sure it is marked as the launch activity. The definition is as follows:

[Activity(
  Label="NationalParks.Droid", MainLauncher = true,
  Icon="@drawable/icon", Theme="@style/Theme.Splash",
  NoHistory=true,
  ScreenOrientation = ScreenOrientation.Portrait)]
public class SplashScreen : MvxSplashScreenActivity
{
    public SplashScreen():base(Resource.Layout.SplashScreen)
    {
    }
}

The iOS startup

The iOS app startup is slightly less automated and is initiated from within the FinishedLaunching() method of AppDelegate:

public override bool FinishedLaunching (
    UIApplication app, NSDictionary options)
{
    _window = new UIWindow (UIScreen.MainScreen.Bounds);

    var setup = new Setup(this, _window);
    setup.Initialize();
    var startup = Mvx.Resolve<IMvxAppStart>();
    startup.Start();

    _window.MakeKeyAndVisible ();

    return true;
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset