Quintic
Forms & MVVM

MVVM

The MVVM (Model : View : VeiewModel) UI design pattern has been around now for a number of years and is recognised as being an important mechanism in separating the responsibilities of UI from Behaviour from Business data. In particular it provides for effective unit testing of the application in isolation from the UI.

As a brief summary the dependency graph flows like this:

View -> ViewModel -> Model

in that the View knows about the ViewModel but nothing else. The ViewModel has no knowledge of the View, but know about the Model and the Model knows only about itself.

An underlying piece of infrastructure to allow this concept to work is that the View needs to be able to update properties of the ViewModel, and know when these properties update (due to possible changes in the Model). It also follows that the ViewModel needs to be aware of any asynchronous changes to properties in the Model not directly initiated by the ViewModel.

When WPF was released with C# back in 2008 this asynchronous comms between the View and ViewModel was achieved by a feature called Data Binding, which provided for properties on UI controls to be bound to properties on the ViewModel, with the binding being one-way or two-way.

This feature is available within Xamarin Form’s XAML implementation.

As a means of understanding MVVM within the Xamarin Forms env, I developed a small app using that paradigm. The following is my experience.

Threads & UI

First and foremost rule to remember is that “Any and all changes to the UI MUST occur on the UI Thread”.

This rule applies to EVERY interaction with the UI. Which is not too bad in a PC env, but with phone and tablet apps, just about everything is a new thread. So, if anything is changing that interacts with the UI, then you have to invoke the change on the UI thread.

This applies to Command “Can Change” events, Data bound objects, anything that you are relying on the UI interacting with.

The annoying part about this exceptional env, is the if you get it wrong, the only feedback you are likely to receive is ‘None’. The update/event/… just will not register and the UI will not react. No error, no warning, no debug info, just no UI reaction.

Single hardest issue to development is the problem of setting a value in the ViewModel and the change not being recognised or actioned in the UI.

Need to jump back to UI Thread. How? Use the Device global (i.e. Static object). Problem is that using the Device object breaks the MVVM paradigm. More importantly, it makes it almost impossible to Unit Test the ViewModel.

You need to wrap this up in a Service.

Same is true of Navigation.

Views and ViewModels

Mentioned above that the View -> ViewModel association relies on heavily on Binding. Binding is a mechanism of saying this piece of information in the View is tied to a Property on the ViewModel. eg

<Label x:Name="TitleLbl" Text="{Binding Title}" />

In the above example the Text in the UI Label will be derived from the ViewModel’s Title property (we will come on to how the ViewModel is identified in a moment)

For this binding to work effectively, the View has to be informed whenever the value of the Title Property in the ViewModel changes. This is signalling is accomplished by:

  1. The ViewModel implementing the IPropertyChanged I/F. Every ViewModel MUST implement this I/F
  2. Implementing a PropertyChanged event to which the View can subscribe
  3. Invoking the PropertyChanged event whenever a Property is changed.

My personal preference is always implementing the above is to create a BaseViewModel class that implements the above, and then derive all my ViewModels from this base class. eg:

public class BaseViewModel : INotifyPropertyChanged
{
    public BaseViewModel()
    {
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Protected again Race Condition
        var propertyChanged = PropertyChanged;

        //Throw back to UI thread.
        Device.BeginInvokeOnMainThread(() =>
        {
            propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
         });
     }
 
    protected bool SetProperty<T>(ref T current, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(current, newValue))
        {
            return false;
        }
        current = newValue;
        OnPropertyChanged(propertyName);
        return true;
    }
}

Then in the concrete ViewModel

   public class MainiewModel : BaseViewModel
     {
         private string _title;

         public DVDViewModel()
         {
         }
 
         public string Title
         {
             get { return _title; }
             set
             {
                 SetProperty(ref _title, value);
             }
         }
     }