Appendix B. Using MVVM Light instead of MvvmCross

This book focuses on building cross-platform apps using MVVM, and although the concepts are agnostic to the framework you’re using, my examples focused on the MvvmCross framework. There are a range of different MVVM frameworks, each with their own quirks, features, and ways of doing things. You’ve seen MvvmCross; this appendix looks at a different framework called MVVM Light.

The question of which framework to use has a standard technology answer—it depends! Each framework has its own strengths and weaknesses. MvvmCross is a heavyweight, opinionated framework. It provides a lot for you out of the box, which has the upside of making it easier to get started because you have less code to write, but the downside is that it’s harder to do things that are different from the MvvmCross way. MVVM Light, on the other hand, is much more lightweight and provides only what you need to implement the basics of MVVM. You have to do more yourself, but in return you have more control.

Sometimes the best reason for choosing a framework is experience. If in the past you’ve built Windows apps using MVVM Light, it makes sense to keep using the framework you know. If you don’t have experience with any MVVM frameworks that support Xamarin apps, you could try out a simple app in each framework to see which feels more comfortable. You can always switch frameworks if needed, but remember that the lighter the framework, the easier it is to change. It’s easier to change from MVVM Light to MvvmCross than the other way around, especially if you’ve used a lot from the MvvmCross ecosystem.

B.1. MVVM Light

MVVM Light is an open source MVVM framework created by Laurent Bugnion, a Cloud Developer Advocate working at Microsoft. As the name suggests, it’s a lightweight framework, in that it doesn’t do too much for you. It just provides you with the basics. Although it’s lightweight, it does have everything you need to build apps. It’s also very mature, having been around for eight years, and it supports all the various Windows technologies as well as Xamarin apps.

Out of the box it provides a base view model to handle property-change notifications, a command implementation, a binding layer, an IoC container, a navigation service to support view-model-first navigation, a dispatcher helper to marshal calls onto the UI thread, and a messenger. For iOS and Android apps it also has helpers for table views and recycler views.

This appendix isn’t a full tutorial on MVVM Light. It just looks at the features MVVM Light provides, showing you how to get started porting Countr to MVVM Light. There’s an example version of Countr using MVVM Light in the source code that accompanies this book, and the MVVM Light website at http://www.mvvmlight.net has tutorials you can use, and a link to a PluralSight course.

B.2. Installing MVVM Light

The easiest way to create an MVVM Light project is to create a standard Xamarin app and then add the MvvmLight NuGet packages. There are two main packages to choose from: MvvmLight or MvvmLightLibs. The MvvmLight NuGet package installs the required libraries and creates some example view and view model files. The MvvmLightLibs package just installs the required libraries. To port Countr, you would remove the MvvmCross packages and then install MvvmLightLibs.

These libraries provide everything necessary for your core cross-platform project as well as your iOS app. For Android, there are two more NuGet packages to install. MvvmLightAndroidSupport provides support for recycler views, and the third-party JimBobBennett.MvvmLight.AppCompat NuGet package provides support for AppCompat.

B.3. The model layer

The only difference in the model layer is the messenger. Just like MvvmCross, MVVM Light has a messenger—GalaSoft.MvvmLight.Messaging.Messenger—and this exposes an interface called IMessenger. This is a simple interface, with methods to register and unregister for a message, and to send a message to all registered recipients. Messages can be any class—there’s no need to derive from a particular base class, unlike MvvmCross with its base MvxMessage class. This means the existing Countr model layer can be reused just by changing the messenger and removing the message base class.

B.4. The view model layer

Just like MvmCross, MVVM Light has a base view-model class, GalaSoft.MvvmLight.ViewModelBase. This class has similar capabilities to the base view model in MvvmCross, providing support for property-changed notifications and a Set method to update a value inside a property if it has changed, raising the property-changed event.

Commands are provided using GalaSoft.MvvmLight.Command.RelayCommand. Just like the MvvmCross MvxCommand class, this command wraps an action. One feature that’s not provided in MVVM Light is support for async commands—there’s no way to create an async RelayCommand that you can await the execution on.

This lack of async support also extends to navigation. MVVM Light has a navigation service, exposed via GalaSoft.MvvmLight.Views.INavigationService, that provides view-model-first navigation, but the methods on this interface aren’t async. Navigation works slightly differently in MVVM Light and MvvmCross. Instead of navigating to a view model, as you do in MvvmCross, you navigate to a page key—a string that defines a view and view-model relationship, and as you’ll see later in this appendix, this is defined in the view layer. In our CountersViewModel, the NavigateTo method would be used when using MVVM Light, with a string-based page key to define where to navigate to, and a parameter can be passed to the target view model. These parameters aren’t passed to a particular method on the target view model; instead, the view that’s navigated to needs to pull out the parameter. You’ll see this later in this appendix.

How you define these page keys is up to you. My preferred way to specify these keys is using the name of the view model, so to navigate to the counter view model, you’d use code such as this:

navigationService.NavigateTo(nameof(CounterViewModel), new Counter());

Closing a view is done with the GoBack method on the navigation service. This will close the current view, showing the previous one.

MVVM Light has an IoC container, accessible using the static Default property on the GalaSoft.MvvmLight.Ioc.SimpleIoc class. View models, services, and repositories can be registered inside this container and resolved using constructor injection, just like with MvvmCross. The difference is that MVVM Light wraps a lot of this in a view-model locator—a static class used to register services and get view models. MvvmCross has some setup code provided by the framework, and inside this setup code you initialize the content of the IoC container. MVVM Light, on the other hand, relies on you doing this inside your own app code. The traditional way is to create a static view-model locator, and inside the static constructor register everything. You then expose methods or properties on the view-model locator to return view models from the IoC container. The following listing shows an example implementation for Countr.

Listing B.1. Registering services and providing properties to get view-model instances
public static class ViewModelLocator                                        1
{
   static ViewModelLocator()
   {
      SimpleIoc.Default.Register<CountersViewModel>();                      2
      SimpleIoc.Default.Register<CounterViewModel>();                       2

      SimpleIoc.Default.Register<ICountersService, CountersService>();      3
      SimpleIoc.Default.Register<ICountersRepository, CountersRepository>();3
      SimpleIoc.Default.Register<IMessenger, Messenger>();                  3
   }

   public static CountersViewModel CountersViewModel                        4
   {                                                                        4
      get { return SimpleIoc.Default.GetInstance<CountersViewModel>(); }    4
   }                                                                        4

   public static CounterViewModel CounterViewModel                          5
   {                                                                        5
      get                                                                   5
      {                                                                     5
         return SimpleIoc.Default                                           5
                         .GetInstanceWithoutCaching<CounterViewModel>();    5
      }                                                                     5
   }                                                                        5
}

  • 1 The view model locator is a static class.
  • 2 The static constructor registers view models in the IoC container.
  • 3 Services and repositories are registered by interface.
  • 4 Returns a singleton instance of the CountersViewModel
  • 5 Returns a new instance of the CounterViewModel

In the static constructor, the view models are registered into the IoC container. The static Default property on the SimpleIoc class returns a singleton instance of the container that you can use anywhere in your code if you want to, although like with the MvvmCross IoC container, constructor injection is preferred as it’s easier to test. The constructor also registers your services, repositories, and any other classes you want to resolve, such as the messenger.

The properties that return view models are used by views to get their binding context, and you’ll see this in action later in this appendix, when we look at views. The thing to note here, though, is that the IoC container has two methods for retrieving objects from it. The first is GetInstance, which returns a singleton, so the same instance every time. The second is GetInstanceWithoutCaching, which returns a new instance every time. We only ever want one counters view model, so the property returns the same instance. For the counter view model, we want a new one every time, so we use GetInstanceWithoutCaching to return a new instance.

Messages, such as the message sent by the counters service whenever the counters are updated, are handled in the view-model layer by registering an action with the messenger using the Register method. This generic method has a generic argument of the type of message you’re registering for, and it takes an action to be executed when the message is received. Unlike the MvvmCross messenger, this doesn’t return a token that you keep hold of to keep the subscription alive. Instead, you also pass the instance of the owner to it, and as long as the owner hasn’t been garbage-collected, the action will be called.

The other difference from MvvmCross is that there’s no way to register to receive a message on the UI thread. Instead, all messages are handled on the thread that was used to send them. To handle messages on the UI thread, use the MVVM Light dispatcher helper. This is a class that’s only available in your platform-specific app code and can marshal calls onto the UI thread. The trick to making this work inside your cross-platform code project is to register a callback inside your view-model locator that’s used to run code on the UI thread, and, as you’ll see later in this appendix, set this callback to use the dispatcher helper. The core project’s view-model locator code is shown in this listing.

Listing B.2. Registering a callback to run code on the UI thread
static Action<Action> dispatcher;                                         1
public static void RegisterDispatcher(Action<Action> dispatcherAction)    1
{                                                                         1
   dispatcher = dispatcherAction;                                         1
}                                                                         1

public static void RunOnUIThread(Action action)                           2
{                                                                         2
   dispatcher(action);                                                    2
}                                                                         2

  • 1 Registers a callback for code to run on the UI thread
  • 2 Uses the callback to run code

The RunOnUIThread method uses the callback to run a given action, so this can be used with messenger to ensure a message is handled on the UI thread:

messenger.Register<CountersChangedMessage>(this, m =>
ViewModelLocator.RunOnUIThread(async () => await LoadCounters()));

B.5. The view layer

As you’d expect, the platform-specific implementation is different on each platform, so let’s look at them one by one, starting with Android.

B.5.1. The Android view and application layer

The MVVM Light Android support comes from a separate NuGet package, and there are two packages to use. MvvmLightAndroidSupport provides Android support for non-AppCompat activities and recycler views, and JimBobBennett.MvvmLight.AppCompat provides support for AppCompat activities. I always recommend using AppCompat to provide the largest amount of OS support, so install both NuGet packages. The layout files and activities created in this book can be pretty much reused, with a few tweaks to make them support MVVM Light instead of MvvmCross.

Setting up the navigation service

In your initial activity, such as your splash-screen activity, initialize the navigation service and set up the mappings from page keys to views. The code to do this is shown in the following listing.

Listing B.3. Setting up the Android navigation service
private static bool initialized;                                         1

protected override void OnCreate(Android.OS.Bundle savedInstanceState)
{
   ...

   if (!initialized)                                                     1
   {                                                                     1
      initialized = true;                                                1

      var navigationService = new AppCompatNavigationService();          2
      navigationService.Configure(nameof(CountersViewModel),             3
                                  typeof(CountersView));                 3
      navigationService.Configure(nameof(CounterViewModel),              3
                                  typeof(CounterView));                  3
      ViewModelLocator.RegisterNavigationService(navigationService);     4
   }
}

  • 1 Ensures that the navigation service is only initialized once
  • 2 Creates the Android platform-specific navigation service
  • 3 Sets up the page keys for the different views
  • 4 Registers the navigation service with the view-model locator

This code registers all the views with the navigation service using a string-based page key, in this case the name of the relevant view model. When you navigate based on a page key, it will create the relevant activity and show that.

With MvvmCross, you set the app start—the view model to navigate to after the splash screen. MVVM Light doesn’t have this. Instead, in the OnResume method of the splash screen, you need to manually navigate to the appropriate page key:

protected override void OnResume()
{
   base.OnResume();
   ViewModelLocator.NavigationService.NavigateTo(nameof(CountersViewModel));
}
Retrieving view models and navigation parameters

The view model for the new view will come from the view-model locator. The following listing shows an example of this for the counters view.

Listing B.4. Getting view models inside Android activities
CounterViewModel viewModel;

protected override void OnCreate(Bundle savedInstanceState)
{
   ...
   viewModel = ViewModelLocator.CounterViewModel;          1
}

  • 1 Gets the view model from the view-model locator

When a parameter is passed to the navigation service, it can be retrieved using the platform-specific GetAndRemoveParameter method. This method is platform specific—it works slightly differently on iOS and Android to handle the different way data is passed around. When navigating from activity to activity in Android, data is passed using an Intent, and the navigation service can use this intent to get the parameter that was passed. The following listing shows how to retrieve the counter parameter in the counter view model.

Listing B.5. Getting the parameter passed to the navigation service
var navigationService = (AppCompatNavigationService)ViewModelLocator
.NavigationService;
var counter = navigationService.GetAndRemoveParameter<Counter>(Intent);
viewModel.Prepare(counter);

Once this parameter has been retrieved, it can be passed to the existing Prepare method on the view model. Unlike MvvmCross, there are no lifecycle methods on the view model that are called automatically by the view. You’ll need to call methods like LoadCounters on the counters view model manually from the OnCreate method of your activity, instead of relying on methods such as Prepare and Initialize being called.

Binding

When using MVVM Light, binding is configured purely in code in your activity—there’s no way to do it in the layout AXML file. Bindings for properties are created using the SetBinding extension method from the GalaSoft.MvvmLight.Helpers namespace, and this binds a property on the view model to a control. Both the view model and control need to be a field or property on the activity. The bindings that are created aren’t stored anywhere by default, so they’ll be garbage-collected if you don’t explicitly store them somewhere in your code (and once they’re garbage-collected, the bindings will no longer work).

The standard pattern is to have a field or property for the view model, a property for the control that retrieves the control from the layout, and a field containing a collection of bindings. The following listing shows how to create the binding for the counter name EditText control in the counter view.

Listing B.6. Binding the counter name to an edit text
EditText _counterName;                                                   1
EditText CounterName => _counterName ??                                  1
   (_counterName = FindViewById<EditText>(Resource.Id.counter_name));    1

readonly List<Binding> bindings = new List<Binding>();                   2

protected override void OnCreate(Bundle savedInstanceState)
{
   ...
   bindings.Add(this.SetBinding(() => viewModel.Name,                    3
                                () => CounterName.Text,                  3
                                BindingMode.TwoWay));                    3
}

  • 1 A property that gets the EditText, caching the value
  • 2 A list of bindings
  • 3 Creates and stores a binding from the Name property on the view model to the text property on the EditText

The SetBinding extension method must be called on the view.

Commands are bound in a different way than other properties. Instead of using SetBinding, the SetCommand extension method is used. It’s called on the widget that you want to configure the command against, such as a button, and the call takes the name of the event to wire up, and the command on the view model to connect to. This mechanism is quite powerful, in that you can wire up any event to a command.

The following listing shows an example of wiring up the click event of the add new counter floating action button to the command on the counters view model.

Listing B.7. Binding the click event of a floating action button to a command
AddCounterButton.SetCommand(nameof(FloatingActionButton.Click),
viewModel.ShowAddNewCounterCommand);
Recycler views

Like MvvmCross, MVVM Light has helpers for recycler views. Instead of providing its own implementation, MVVM Light provides an implementation of a recycler view adapter that will keep the control in sync with an observable collection. The adapter is created using an extension method on ObservableCollection called GetRecyclerAdapter. This method takes a layout for each item and an action that binds an item in the collection to that layout, and it returns an adapter that can be set on the recycler view.

Listing B.8 shows how this adapter is created, and listing B.9 shows an implementation for the action that binds each item.

Listing B.8. Creating an adapter from an observable collection
ObservableRecyclerAdapter<CounterViewModel, CachingViewHolder> adapter;

protected override async void OnCreate(Bundle savedInstanceState)
{
   ...
   adapter = viewModel.Counters
                      .GetRecyclerAdapter(BindViewHolder,
                                          Resource.Layout.counter_recycler_view);
   RecyclerView.SetAdapter(adapter);
   ...
}
Listing B.9. The BindViewHolder method resets the bindings
void BindViewHolder(CachingViewHolder holder,
                    CounterViewModel counterVm,
                    int position)
{
   var name = holder.FindCachedViewById<TextView>
                                   (Resource.Id.counter_name);
   var count = holder.FindCachedViewById<TextView>
                                   (Resource.Id.counter_count);
   var incrementButton = holder.FindCachedViewById<ImageButton>
                                   (Resource.Id.add_image);

   holder.DeleteBinding(name);
   holder.DeleteBinding(count);

   holder.SaveBinding(name,
                      new Binding<string, string>(counterVm,
                                                  () => counterVm.Name,
                                                  name,
                                                  () => name.Text,
                                                  BindingMode.OneWay));
   holder.SaveBinding(count,
                      new Binding<int, string>(counterVm,
                                               () => counterVm.Count,
                                               count,
                                               () => count.Text,
                                               BindingMode.OneWay));
   incrementButton.SetCommand(nameof(ImageButton.Click),
                              counterVm.IncrementCommand);
}

When an item in the recycler view is recycled, it needs to have all its bindings reset to stop it showing the old item. The code in BindViewHolder is called by the adapter, passing in a view holder, the item view model, and the position in the collection. The view holder is a helper class that stores the underlying controls to avoid a UI lookup each time the item is re-bound, along with a list of bindings. These bindings are deleted, and new bindings are created to the new item.

Configuring the dispatcher helper

You have two options for the dispatcher helper, depending on whether you want to support AppCompat or not: JimBobBennett.MvvmLight.AppCompat.AppCompatDispatcherHelper is the one to use when using AppCompat, and GalaSoft.MvvmLight.Threading.DispatcherHelper is for when you’re not using AppCompat. Both variants have a static CheckBeginInvokeOnUI method that takes an action and runs it on the UI thread, and you can register this method with the view-model locator to run code on the UI thread:

ViewModelLocator.RegisterDispatcher(DispatcherHelper.CheckBeginInvokeOnUI);

This code needs to be added to the splash screen when setting up the navigation setting.

B.5.2. The iOS view and application layer

Unlike Android, iOS support is in the standard MvvmLightLibs NuGet package, so there’s nothing extra to install. The view controllers can be reused from the MvvmCross version of Countr with a few modifications, and so can the storyboards.

Setting up the navigation service

Just like Android, there’s a platform-specific navigation service that needs to be configured, with views set up via page keys. The difference is that there’s no inherent navigation in iOS like there is in Android. In Android, one activity can navigate to another, but in iOS you need to put your view controllers inside a UINavigationController to get the same navigation stack. MvvmCross will create one of these for you, but with MVVM Light you need to create it yourself. MVVM Light also expects the view controllers in your navigation stack to be in the same storyboard, instead of in one storyboard per view, but you can copy and paste from the existing storyboards.

This means that you need only one storyboard for your app, and this storyboard will need to contain a navigation controller marked as the initial view controller, as well as all the view controllers for all your views, as shown in figure B.1. The counters view controller will need to be set as the root view controller in the navigation controller, so that this view is shown on startup (remember, MVVM Light doesn’t define an app start view model). This storyboard will also need to be configured as the main interface for your app in the info.plist file (figure B.2).

Figure B.1. The main storyboard needs to contain a navigation controller as the initial view controller, have the counters view as the root view controller in the navigation controller, and contain the counter view.

Figure B.2. The main interface needs to be set to your storyboard in the info.plist file.

When you construct the navigation service, pass in the navigation controller, so that it can be used to navigate between views. By the time the FinishedLaunching method in the AppDelegate is called, the main storyboard will have been loaded, and the root view controller of the app’s window will be the navigation controller. The following listing shows how this can be used to configure the navigation controller.

Listing B.10. Navigation service created using the root view controller of the window
public override bool FinishedLaunching(UIApplication application,
                                     NSDictionary launchOptions)
{
   var navigationService = new NavigationService();
   navigationService.Initialize((UINavigationController)Window
.RootViewController);
   ...
}

The views are then registered with the navigation service, and the navigation service is registered with the view-model locator in the same way as on Android.

Retrieving view models and navigation parameters

Just like on Android, view models are retrieved from the view-model locator. The navigation parameters are also retrieved in a similar way, except the view controller is passed to the GetAndRemoveParameter method on the platform-specific navigation service, instead of to an Intent, as shown in the following listing.

Listing B.11. Getting the parameter passed to the navigation service
var navigationService = (NavigationService)ViewModelLocator.NavigationService;
var counter = (Counter)navigationService.GetAndRemoveParameter(this);
viewModel.Prepare(counter);
Binding

Binding on iOS uses the same mechanism as Android—extension methods that return bindings that you persist in your view controller. The only real difference is that you don’t need to manually create properties for the controls; these are created for you when you name a control in the storyboard designer. Commands are also bound using the same extension method as Android.

Table views

MVVM Light provides helpers to help bind table view controllers to observable collections. Unlike like with Android, there’s a base class to use for your table view controller, ObservableTableViewController<T>, where the generic parameter T is the view model for each item.

The table view source is created using an extension method on an observable collection called GetTableViewSource, just like how the recycler view adapter was created on Android. This extension method takes an action to bind a cell and a cell prototype identifier. You can optionally pass a func that returns a new ObservableTableViewSource<T>, which is used if you need to override the behavior of the basic table view source (for example, if you need to implement swipe to delete). This method returns a fully configured table view source that you can pass to the table view and that will keep your table in sync with your observable collection. The following listing shows an example of this.

Listing B.12. Creating the table view source from the counters observable collection
UITableViewSource tableViewSource;

public override async void ViewDidLoad()
{
   ...
   tableViewSource = viewModel.Counters.GetTableViewSource(BindCounterCell,
                                                           "CounterCell");
   TableView.Source = tableViewSource;
}

In this code, BindCounterCell is a method that binds the cell to the item view model, and "CounterCell" is the prototype identifier for the table view cell class that’s defined on the storyboard.

Unlike on Android, there’s no view holder to store all the bindings against each cell, so it’s best to use the cell class to manage the bindings. This way, each cell can store a list of bindings that are then deleted and recreated when the binding context changes. The following listing shows how this can be implemented in the counter cell.

Listing B.13. Binding a counter view model to the counter cell
List<Binding> bindings = new List<Binding>();
CounterViewModel viewModel;

public void Bind(CounterViewModel counterVm)
{
   foreach (var binding in bindings)                              1
      binding.Detach();                                           1
   bindings.Clear();                                              1

   viewModel = counterVm;

   bindings.Add(this.SetBinding(() => viewModel.Name,             2
                                () => CounterName.Text));         2
   bindings.Add(this.SetBinding(() => viewModel.Count,            2
                                () => CounterCount.Text));        2
   IncrementButton.SetCommand(viewModel.IncrementCommand);        2
}

  • 1 Clears out any existing bindings
  • 2 Rebinds the counter name, count, and increment command

This Bind method can then be called from the BindCounterCell action passed to the GetTableViewSource extension method:

private void BindCounterCell(UITableViewCell cell,
                           CounterViewModel counterVm,
                           NSIndexPath path)
{
   ((CounterTableViewCell)cell).Bind(counterVm);
}
Configuring the dispatcher helper

The iOS dispatcher helper is the static class GalaSoft.MvvmLight.Threading.DispatcherHelper, and it’s initialized with an object owned by the main thread, so that it knows which thread should be used to dispatch calls to the UI thread. Once initialized, it has a CheckBeginInvokeOnUI method, just as on Android, and you can register this in the view-model locator, as follows.

Listing B.14. Registering the iOS dispatcher helper
public override bool FinishedLaunching(UIApplication application,
                                     NSDictionary launchOptions)
{
   ...
   DispatcherHelper.Initialize(application);
   ViewModelLocator.RegisterDispatcher(DispatcherHelper.CheckBeginInvokeOnUI);
   ...
}

Summary

MvvmCross and MVVM Light have similarities in that they provide everything you need to build Xamarin apps using MVVM-based view models, a navigation service, a messaging service, commands, and helpers for list controls. They differ in the implementation and the details of what they provide, as outlined in the following tables.

Table B.1. Model layer differences between MvvmCross and MVVM Light
 

MvvmCross

MVVM Light

Messaging MvxMessenger Using the MvvmCross messenger, you can subscribe to messages on the UI thread or a background thread. All messages need to derive from MvxMessage. Messenger Using the MVVM Light messenger, you can only subscribe to messages on the current thread, and you have to use a platform-specific dispatcher helper to marshal code onto the UI thread. Messages can be any object.
Table B.2. View-model layer differences between MvvmCross and MVVM Light
 

MvvmCross

MVVM Light

Base view model class MvxViewModel Provides property-changed notifications and a SetProperty method to update a property and raise the notification. Also provides lifecycle methods that are called on navigation and as the view lifecycle happens. ViewModelBase Provides property-changed notifications and a Set method to update a property and raise the notification. No lifecycle methods.
Commands MvxCommand and MvxAsyncCommand Two command implementations are provided, both taking an action to run when the command is executed and optionally a func to evaluate to see if the command can execute. One implementation is synchronous, and the other is async and supports async and await in the action that’s run on execution, meaning that the Execute method won’t complete until the async implementation has finished. RelayCommand Only one command implementation is provided, taking an action to run when the command is executed and optionally a func to evaluate to see if the command can execute. This doesn’t support async, so if the implementation uses async and await, the Execute method will complete as soon as the first await is hit.
Navigation IMvxNavigationService The MvvmCross navigation service is created automatically and provides view model navigation, and has async navigation methods. View models are created on navigation and made available to the views for binding. Parameters passed to the next view model are handled in the view model. INavigationService The MVVM Light navigation service has to be manually created. Navigation is via a string-based page key instead of via view models, and the navigation methods don’t have async support. View models aren’t created on navigation; a view-model locator is used to get the view model for a view. Parameters passed to the next view model need to be retrieved in the view and passed to the view model.
IoC Mvx An IoC container is provided with a single place to register items in it. View models don’t need to be registered, MvvmCross classes (such as the navigation service) are automatically registered, and classes with the same name can be registered in bulk. If the class being registered needs to be a singleton, this is controlled at registration time. SimpleIoc This IoC container is one that you populate manually, usually in the view model locator, but there’s no one predefined place to do it. You have to manually register everything, including view models. Access to a singleton instance or multiple instances is handled when retrieving items from the container.
Table B.3. Android view layer differences between MvvmCross and MVVM Light
 

MvvmCross

MVVM Light

Views Views are discovered by name, so the view for MyViewModel is called MyView. The first view is shown based on the start view model registered in the cross-platform app setup. The view model for a view is automatically set. Views are registered with the navigation service with a string page key. The first view needs to be manually shown. The view model for a view needs to be manually retrieved from the view-model loader and set.
Binding Controls can be bound in the layout AXML files or in code. All binding happens in code.
Recycler views There’s a set of base classes for recycler views and their adapters that provide binding to observable collections. The standard recycler view is used with a custom adapter that binds to an observable collection.

Table B.4. iOS view layer differences between MvvmCross and MVVM Light
 

MvvmCross

MVVM Light

Views Views are discovered by name, so the view for MyViewModel is called MyView. The first view is shown based on the start view model registered in the cross-platform app setup. Each view has its own storyboard file. The view model for a view is automatically set. Views are registered with the navigation service with a string page key. One storyboard is used for everything, and this needs to include a navigation view controller that the navigation service can use. The first view needs to be set as the root of the navigation service or be manually navigated to. The view model for a view needs to be manually retrieved from the view-model loader and set.
Binding All binding happens in code. All binding happens in code.
Table views There’s a set of base classes for table view controllers and their data sources that provide binding to observable collections. There’s a set of base classes for table view controllers and their data sources that provide binding to observable collections.
..................Content has been hidden....................

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