Access a Control Method from a ViewModel

Making a control in your view MVVM friendly by allowing binding to your ViewModel, can be done in several different ways. When developing in the MVVM pattern, you have no access to the View and hence no access to the methods on a Control. Most controls in Xamarin Forms have bindable properties but some do not. In order to keep to our MVVM Pattern there are numerous ways we can access the control’s methods. I have these ordered from my least preferable to my most preferable.

phone_laptop_side

For a common example I will be using the GoBack method of the WebView, which is only accessible directly via the control.

Boolean Flip

A common recommendation you might see around is extending the control and adding a boolean to it. First we must extend the control and add a boolean property of IsGoBack.

public class CustomWebView : WebView
{
    public bool IsGoBack
    {
        get { return (bool)GetValue(IsGoBackProperty); }
        set { SetValue(IsGoBackProperty, value); }
    }

    public static BindableProperty IsGoBackProperty = BindableProperty.Create(nameof(IsGoBack), typeof(bool), typeof(EnhancedWebView), propertyChanged: (bindable, oldValue, newValue) =>
    {
        var webView = binadable as CustomWebView;
        webView.GoBack();
    });
}

Then in your View you just need to bind this property to a propery in your ViewModel.

<control:CustomWebView IsGoBack="{Binding IsGoBack}" />

Now doing this in your ViewModel will cause the PropertyChanged event to be fired and you can call the GoBack method.

private bool _isGoBack;
public string IsGoBack
{
    get
    {
         return _isGoBack;
    }
    set
    {
         _isGoBack = value;
         OnPropertyChanged();
    }
}

// Then call anywhere in your ViewModel by doing
IsGoBack = !IsGoBack;

Pros

  • Keeps your View and ViewModel separated

Cons

  • Extra steps and complexity in relaying a command

Messenger

Messaging Center is a Xamarin Forms implementation of a publish and subscribe model. First we subscribe to an event to listen to in the View.

MessagingCenter.Subscribe<object>(this, "GoBack", (sender) => {
 webView.GoBack();
});

Then when we need to call this, we publish a message.

MessagingCenter.Send<object>(this, "GoBack");

Pros

  • Easy to implement

Cons

  • You need to subscribe and listen to events in your View.
  • You don’t know what is listening or can not get any direct response back.

Supervising Controller Pattern

This Supervising Controller Pattern in this context is when the ViewModel will be aware of an interface to the View and the View will inject itself back into the ViewModel.

First we need to apply an interface to the Page (View).

public interface IView 
{
    void GoBack();
}

Then we implement the interface on the View.

public class MainPage: ContentPage, IView

Then we send the View back to the ViewModel.

public MainPage()
{
    InitializeComponent();
    this.BindingContext = MainViewModelInstance;
    MainViewModelInstance.View = this;
}

Finally in the ViewModel we can call the View as needed to interact with the control.

public IView View { get; set; }

View.GoBack();

Pros

  • No modification or extension of the control

Cons

  •  Must pass the view back into the ViewModel, which ties the ViewModel type to the View.

Command Chaining

Command chaining is the most MVVM friendly approach as it leaves everything to the binding system to connect up and the View and ViewModel still have no direct knowledge of each other. The only issue is you must extend the control and it adds a bit of extra complexity.

First we need to extend the control to add a new Bindable Property with a OneWayToSource. This ensures this property sends its value back to the ViewModel and not the other way around.

public static BindableProperty GoBackCommandProperty =
 BindableProperty.Create(nameof(GoBackCommand), typeof(ICommand), typeof(CustomWebView), null, BindingMode.OneWayToSource);

public ICommand GoBackCommand
{
    get { return (ICommand)GetValue(GoBackCommandProperty); }
    set { SetValue(GoBackCommandProperty, value); }
}

Then we need to assign the GoBack function to the Bindable Property.

public CustomWebView()
{
    GoBackCommand = new Command(() =>
                    {
                         this.GoBack();
                    });
}

Then we add a GoBackCommand to the ViewModel.

public ICommand GoBackCommand
{
    get { return GetValue<ICommand>(); }
    set { SetValue(value); }
}

Now we can Bind the Command directly to another Command that we can execute.

<control:CustomWebView GoBackCommand="{Binding GoBackCommand, Mode=OneWayToSource}" />

<Button Text="Go Back!" Command="{Binding GoBackCommand}" />

Pros

  • View and ViewModel have no reference to each other, everything is connected via the Binding system.

Cons

  • Can be a bit complex to implement and debug
Microsoft MVP | Xamarin Certified Developer |
Exrin Creator | Xamarin Forms Developer | Melbourne, Australia

Related Posts

Leave A Comment?