Simplifying Events with Commanding

David Britch

Utilizing data binding in Xamarin.Forms applications can greatly simplify app development by automatically synchronizing an app’s data to its user interface with very little work. Previously, we took a look at setting up your project to begin data binding and then saw it in action in an app. We then explored more advanced data binding scenarios where values are formatted and converted as they are passed between source and target by the binding engine.

In this blog post, I’m going to explore a Xamarin.Forms feature called commanding, that allows data bindings to make method calls directly to a ViewModel.

Introduction to Commanding

The traditional approach for executing a method in response to an interaction with the UI is to call the method from a Clicked event handler of a Button or a Tapped event handler of a TapGestureRecognizer. However, with commanding, data bindings can make method calls directly to a ViewModel from the following classes:

  • Button
  • MenuItem
  • ToolbarItem
  • SearchBar
  • TextCell
  • ImageCell
  • ListView
  • TapGestureRecognizer

To support commanding, two public properties are defined on the majority of these classes:

  • Command, of type System.Windows.Input.ICommand.
  • CommandParameter, of type object.

Implementing a Command

In order to implement commanding, a ViewModel should define one or more properties of type ICommand. The ICommand interface defines two methods and one event:

public interface ICommand
{
   void Execute(object arg);
   bool CanExecute(object arg)
   event EventHandler CanExecuteChanged; 
} 

The Command and Command<T> classes provided by Xamarin.Forms implement the ICommand interface, where T is the type of the arguments to Execute and CanExecute. As well as implementing the ICommand interface, these classes also include a ChangeCanExecute method, which causes the Command object to fire the CanExecuteChanged event.

Within a ViewModel, there should be an object of type Command or Command<T> for each public property in the ViewModel of type ICommand. The Command or Command<T> constructor requires an Action callback object, that is called when the Button calls the ICommand.Execute method. The CanExecute method is an optional constructor parameter, and takes the form of a Func that returns a bool.

The following code example shows a sample application command implementation that’s used to calculate a square root:

public class DemoViewModel : INotifyPropertyChanged
{
	public int Number { get; set; }
	public double SquareRootResult { get; private set; }
	public ICommand SquareRootCommand { get; private set; }
    ...

	public DemoViewModel ()
	{
		Number = 25;
		SquareRootCommand = new Command (CalculateSquareRoot);
        ...
	}

	void CalculateSquareRoot ()
	{
		SquareRootResult = Math.Sqrt (Number);
		OnPropertyChanged ("SquareRootResult");			
	}
    ...
}

The SquareRootCommand is data bound to the Command property of a Button, as shown in the following code example from the sample application:

<Label Text="Demo 1 - Command" FontAttributes="Bold" />
<StackLayout Orientation="Horizontal">
	<Label Text="Enter number:" />
	<Entry Text="{Binding Number, Mode=TwoWay}" WidthRequest="50" />
</StackLayout>
<Button Text="Calculate Square Root" Command="{Binding SquareRootCommand}" HorizontalOptions="Center" />
<StackLayout Orientation="Horizontal">
	<Label Text="Square root =" />
	<Label Text="{Binding SquareRootResult}" />
</StackLayout>

When the Button is clicked, it calls the ICommand.Execute method of the object bound to its Command property. Therefore, the CalculateSquareRoot method is called, with the value of the Number property being used in the calculation. The square root of this value is calculated, and the Label that binds to the SquareRootResult property is updated with the result.

The following screenshots show the result of the SquareRootCommand being executed:

commanding-demo1

Passing a Parameter to a Command

A parameter can be passed to an ICommand.Execute method by using the Command<T> class to instantiate the command. The following code example shows a sample application command implementation that’s used to calculate a square root of a value passed into the command as a parameter:

public class DemoViewModel : INotifyPropertyChanged
{
    public double SquareRootWithParameterResult { get; private set; }
    public ICommand SquareRootWithParameterCommand { get; private set; }
    ...

    public DemoViewModel ()
    {
        SquareRootWithParameterCommand = new Command<string> (CalculateSquareRoot);
        ...
    }

    void CalculateSquareRoot (string value)
    {
        double num = Convert.ToDouble (value);
        SquareRootWithParameterResult = Math.Sqrt (num);
        OnPropertyChanged ("SquareRootWithParameterResult");            
    }
    ...
}

The SquareRootWithParameterCommand is data bound to the Command property of a Button, as shown in the following code example from the sample application:

<Label Text="Demo 2 - Command with Parameter" FontAttributes="Bold" />
<StackLayout Orientation="Horizontal">
	<Label Text="Enter number:" />
	<Entry x:Name="entry" Text="100" WidthRequest="50" />
</StackLayout>
<Button Text="Calculate Square Root" 
        Command="{Binding SquareRootWithParameterCommand}" 
        CommandParameter="{Binding Source={x:Reference entry}, Path=Text}" HorizontalOptions="Center" />
<StackLayout Orientation="Horizontal">
	<Label Text="Square root =" />
	<Label Text="{Binding SquareRootWithParameterResult}" />
</StackLayout>

As well as the Button binding its Command property to the SquareRootWithParameterCommand, it also passes the Text property value of the Entry to the SquareRootWithParameterCommand through its CommandParameter property.

When the Button is clicked, it calls the ICommand.Execute method of the object bound to its Command property. The argument to the Execute method is the object set to the CommandParameter property of the Button. Therefore, when the Button is clicked, the CalculateSquareRoot method is called, with the value that the CommandParameter of the Button binds to being passed as a parameter to the method. The square root of this value is calculated, and the Label that binds to the SquareRootWithParameterResult property is updated with the result.

The following screenshots show the result of the SquareRootWithParameterCommand being executed:

commanding-demo2

Calling an Asynchronous Method

An asynchronous method can be invoked by a command by using the async and await keywords when specifying the command’s Action callback. This indicates that the callback is a Task and should be awaited. The following code example shows a sample application command implementation that simulates a file downloading through an asynchronous method:

public class DemoViewModel : INotifyPropertyChanged
{
    ...
    bool canDownload = true;
    string simulatedDownloadResult;

    public string SimulatedDownloadResult {
        get { return simulatedDownloadResult; }
        private set {
            if (simulatedDownloadResult != value) {
                simulatedDownloadResult = value;
                OnPropertyChanged ("SimulatedDownloadResult");
            }
        }
    }

    public ICommand SimulateDownloadCommand { get; private set; }

    public DemoViewModel ()
    {
        ...
        SimulateDownloadCommand = 
            new Command (async () => await SimulateDownloadAsync (), () => canDownload);
    }

    async Task SimulateDownloadAsync ()
    {
        CanInitiateNewDownload (false);
        SimulatedDownloadResult = string.Empty;
        await Task.Run (() => SimulateDownload ());
        SimulatedDownloadResult = "Simulated download complete";
        CanInitiateNewDownload (true);
    }

    void CanInitiateNewDownload (bool value)
    {
        canDownload = value;
        ((Command)SimulateDownloadCommand).ChangeCanExecute ();
    }

    void SimulateDownload ()
    {
        // Simulate a 5 second pause
        var endTime = DateTime.Now.AddSeconds (5);
        while (true) {
            if (DateTime.Now >= endTime) {
                break;
            }
        }
    }
    ...
}

The SimulateDownloadCommand is data bound to the Command property of a Button, as shown in the following code example from the sample application:

<Label Text="Demo 3 - Async Command with CanExecute" FontAttributes="Bold" />
<Button Text="Simulate 5 Second Download" HorizontalOptions="Center"
        Command="{Binding SimulateDownloadCommand}" />
<StackLayout Orientation="Horizontal">
	<Label Text="Result: " />
	<Label Text="{Binding SimulatedDownloadResult}" />
</StackLayout>

When the Button is clicked the SimulateDownloadAsync method is invoked and the Button is disabled and re-enabled once the five second simulation has completed. This is achieved by specifying a ICommand.CanExecute method in the SimulateDownloadCommand constructor. The Button calls CanExecute when its Command property is first set. If CanExecute returns false, the Button disables itself and doesn’t generate Execute calls. The Button also handles the ICommand.CanExecuteChanged event. Therefore, whenever the ViewModel fires the CanExecuteChanged event by calling the Command.CanExecuteChanged method, the Button calls CanExecute again to determine if it should be enabled.

The following screenshots demonstrate the disabled Button, while the simulated download asynchronously executes:

commanding-demo3

Note that because the simulated download is executed on a background thread, it’s still possible to interact with the UI while the simulated download is executing.

Wrapping Up

Commanding allows data bindings to make method calls directly to a ViewModel and is supported by eight Xamarin.Forms classes. To implement commanding, a ViewModel defines one or more properties of type ICommand, which are then connected to the Command properties of UI controls through data binding. Within the ViewModel, there should then be an object of type Command or Command<T> for each public property in the ViewModel of type ICommand.

For more information about asynchronous programming, see our Async Support Overview. For more information about data binding, see Data Binding Basics. For more information about data binding and MVVM, see From Data Bindings to MVVM. Xamarin University also provides a class on data binding in Xamarin.Forms.

0 comments

Discussion is closed.

Feedback usabilla icon