left-icon

Xamarin Community Toolkit Succinctly®
by Alessandro Del Sole

Previous
Chapter

of
A
A
A

CHAPTER 6

Supporting Advanced Model-View-ViewModel Development

Supporting Advanced Model-View-ViewModel Development


Model-View-ViewModel (MVVM) is a programming pattern that allows for separating data (Model), logic (ViewModel), and user interface (View), and is generally available to development platforms based on XAML, such as Xamarin.Forms, Windows Presentation Foundation (WPF), and Universal Windows Platform (UWP).

One of the biggest benefits of using MVVM is that it is possible to work on the user interface without touching the code for the logic, making it possible for graphic designers to work on the XAML markup using specific tools. Developers can also work on the logic and the data architecture without changing the user interface, so there is also some kind of role separation.

This chapter discusses how the Xamarin Community Toolkit enhances MVVM development, especially for developers who need to create their own framework and cannot use popular libraries.

Note: It is not possible to explain the Model-View-ViewModel pattern in this chapter, so here I assume you have at least a basic knowledge of how it works.

The commanding pattern

Actually, MVVM is based on another pattern called commanding, which relies on exposing properties of type ICommand from ViewModels rather than handling events in a view’s code behind. Commanding is widely used in popular, ready-to-use MVVM frameworks such as Prism and MVVMLight, and it has been discussed in both the documentation and in this blog post by Microsoft.

The main purpose of commanding is simplifying the way events are handled from a logic-separation point of view, and it can certainly be used outside of MVVM, or if you prefer (or need) to create your own MVVM framework.

In order to make the implementation of commanding simpler, it is common practice to implement a very popular behavior called EventToCommandBehavior, which quickly allows the generating of a command for an event. This behavior is already available inside existing libraries, such as the ones mentioned previously, but you would need to implement it on your own in other cases.

So that you don’t have to do this every time for each of your projects, the Xamarin Community Toolkit helps by providing a reusable implementation of the EventToCommandBehavior, with the addition of other classes and interfaces that will be discussed in this chapter. For your reference, Figure 45 shows how the example looks like in the sample app.

Handling events as commands

Figure 45: Handling events as commands

Actually, you will not notice any difference in the functionalities because the counter is simply incremented at every mouse click. But behind the scenes, this is leveraging a more data-oriented approach.

Using EventToCommandBehavior to map events to commands

The EventToCommandBehavior is demonstrated in the Behaviors\EventToCommandBehaviorPage.xaml file of the sample project. Code Listing 9 shows the full XAML code for the page.

Code Listing 9

<?xml version="1.0" encoding="UTF-8"?>

<pages:BasePage xmlns="http://xamarin.com/schemas/2014/forms"

               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

               xmlns:xct="http://xamarin.com/schemas/2020/toolkit"

               xmlns:pages="clr-namespace:Xamarin.CommunityToolkit.Sample.Pages"

               xmlns:vm="clr-namespace:Xamarin.CommunityToolkit.Sample.ViewModels.Behaviors"

          x:Class="Xamarin.CommunityToolkit.Sample.Pages.Behaviors.EventToCommandBehaviorPage">

    <pages:BasePage.BindingContext>

        <vm:EventToCommandBehaviorViewModel />

    </pages:BasePage.BindingContext>

    <ContentView Padding="{StaticResource ContentPadding}">

        <StackLayout VerticalOptions="CenterAndExpand"

                    Spacing="20">

            <Label Text="This sample demonstrates how to use EventToCommandBehavior. Here we observe Clicked event of the button and trigger IncrementCommand from ViewModel." />

            <Label Text="{Binding ClickCount,

                    StringFormat='{0} clicks'}" />

            <Button Text="Click Me"

                   TextColor="White"

                   BackgroundColor="{StaticResource

                    NormalButtonBackgroundColor}">

                <Button.Behaviors>

                    <xct:EventToCommandBehavior

                       EventName="Clicked"

                       Command="{Binding IncrementCommand}" />

                </Button.Behaviors>

            </Button>

        </StackLayout>

    </ContentView>

</pages:BasePage>

As you can see, the BindingContext of the page has been assigned with a sample ViewModel called EventToCommandBehaviorViewModel. This will be discussed shortly with a focus on the architecture of the behavior. For now, it is important to emphasize that this ViewModel represents the data context of the page, and that it exposes a command called IncrementCommand, whose purpose is incrementing a counter.

The key point of the code is in the Button. If you look at Code Listing 9, in the Behaviors collection, you can see how the EventToCommandBehavior class is used to map the Clicked event of the button, via the EventName property, to a command in the ViewModel. This means that when the Clicked event of the button is fired, the behavior redirects the action to the bound command instead of an event handler.

You might argue that the Button already has a Command property, and that using the behavior is not necessary, which is certainly true, but obviously this has been used to provide the simplest, yet most effective example possible.

In real-world scenarios, it is very common to use this behavior with other views where there is no built-in support for commands. For instance, in a ListView you could use the behavior as follows.

<ListView>

    <ListView.Behaviors>

        <xct:EventToCommandBehavior

            EventName="SelectionChanged"

            Command="{Binding SelectionChangedCommand}" />

    </ListView.Behaviors>

</ListView>

This would allow you to avoid handling an event in a view’s code-behind to manage data or logic that is in the ViewModel. At the same time, this approach would make it possible to handle the action in the ViewModel, which is the place where the logic is, favoring layer separation and without violating the principles of the MVVM pattern.

Retrieving event arguments

If you used regular event handlers to intercept user actions instead of using the EventToCommandBehavior class, you could also get the event arguments, typically a specialized instance of the EventArgs class, that contain information on the current object.

For example, when the user taps an item in the list, the ItemTapped event is raised and information on the tapped item is contained in the ItemTappedEventArgs object. When the user selects an item in the list, the ItemSelected event is raised and information on the selected item is contained in the ItemSelectedEventArgs object.

The Xamarin Community Toolkit provides two converters that work in combination with the EventToCommandBehavior class and allow for retrieving the appropriate event arguments. They are demonstrated in the Pages\Converters\ItemTappedEventArgsPage.xaml and Pages\Converters\ItemSelectedEventArgsPage.xaml files, and they work very similarly. Both assign their result to the EventArgsConverter property of the EventToCommandBehavior instance, and they work as follows.

<ListView.Behaviors>

    <xct:EventToCommandBehavior EventName="ItemTapped"

         Command="{Binding ItemTappedCommand}"

         EventArgsConverter="{StaticResource

         ItemTappedEventArgsConverter}" />

</ListView.Behaviors>

<ListView.Behaviors>

    <xct:EventToCommandBehavior EventName="ItemSelected"

         Command="{Binding ItemSelectedCommand}"

         EventArgsConverter="{StaticResource

         ItemSelectedEventArgsConverter}" />

</ListView.Behaviors>

When you make this assignment, the EventToCommandBehavior instance checks if the EventArgsConverter property contains an object. If any, the assigned converter is called and the retrieved data can be accessed. An example is in the code-behind file of both sample pages, and the following code shows how this happens for the ItemTappedEventArgsConverter inside the ItemTappedEventArgsViewModel class.

public ICommand ItemTappedCommand { get; } =

     CommandFactory.Create<Person>(person =>

     Application.Current.MainPage.DisplayAlert(

          "Item Tapped: ", person?.Name, "Cancel"));

The ItemTappedCommand can access the bound instance of the Person class because the behavior has retrieved the appropriate ItemTappedEventArgs instance. The ItemSelectedEventArgsConverter works the same way; only the returned object is different.

Architecture: The CommandFactory class

The support provided by the Xamarin Community Toolkit to MVVM development is not limited to the EventToCommandBehavior class. The Xamarin.CommunityToolkit.ObjectModel namespace offers additional objects that can be used in a variety of scenarios.

In order to understand more, let’s have a look at the ViewModel bound to the page discussed previously, whose code is shown in Code Listing 10. The code file is called EventToCommandBehaviorViewModel.cs, and it is located under ViewModels\Behaviors.

Code Listing 10

using System.Windows.Input;

using Xamarin.CommunityToolkit.ObjectModel;

namespace Xamarin.CommunityToolkit.Sample.ViewModels.Behaviors

{

     public class EventToCommandBehaviorViewModel : BaseViewModel

     {

          int clickCount;

          public EventToCommandBehaviorViewModel() =>

               IncrementCommand =

                  CommandFactory.Create(() => ClickCount++);

          public int ClickCount

          {

               get => clickCount;

               set => SetProperty(ref clickCount, value);

          }

          public ICommand IncrementCommand { get; }

     }

}

The IncrementCommand property, of type ICommand, is the command that is bound to the button in the user interface. Different from a common MVVM approach, where the property would be of type Command and where this would be instantiated with a new instance of the Command object, with the Xamarin Community Toolkit tools, you can create an instance of the command by invoking the Create method from the CommandFactory class.

In this particular example, the method takes an Action as the argument, whose purpose is incrementing the ClickCount property by one unit. However, the Create method has 16 more overloads. These overloads have in common the possibility to take an Action as the first parameter that represents the Execute call of a command, and a Func<bool> as the second parameter that represents the CanExecute value of a command.

Among the method overloads, there are some that allow for creating instances of asynchronous commands, which really makes a difference if compared to the classical commanding pattern implementation.

Implementing asynchronous commands

Though it is possible to execute asynchronous tasks as the target action of Command objects, these were not really thought to be asynchronous. With regard to this, the Xamarin Community Toolkit makes another step forward by providing the IAsyncCommand interface, which allows for implementing asynchronous commands.

Tip: The next example is not part of the official project, so here you learn something outside of the sample repository.

There are specific overloads of the CommandFactory.Create method that allow for instantiating asynchronous commands. For a better understanding, consider the following code, whose purpose is still incrementing the value for the ClickCount property, but asynchronously.

public IAsyncCommand IncrementCommandAsync { get; }

public EventToCommandBehaviorViewModel()

{

     IncrementCommandAsync = CommandFactory.Create(async () =>

                              await IncrementCounterAsync());

}

public async Task IncrementCounterAsync()

{

     await Task.Run(() => ClickCount++);

}

The command is declared as of type IAsyncCommand, and an instance is created via an overload of the CommandFactory.Create method. The action to be executed is an asynchronous method, invoked via the await operator. The benefit is having the possibility of implementing asynchronous commands very quickly.

In addition, following the existing Xamarin.Forms code base, the Xamarin Community Toolkit also exposes the AsyncCommand class, which implements the IAsyncCommand interface and can be used like you would do with a normal Command instance. Here is an example:

public EventToCommandBehaviorViewModel()

{

     IncrementCommandAsync =

          new AsyncCommand(() => IncrementAcounterAsync());

}

public IAsyncCommand IncrementCommandAsync { get; }

public async Task IncrementAcounterAsync()

{

     await Task.Run(() => ClickCount++);

}

This approach is useful if you want to stay familiar with the syntax normally used with synchronous commands. Remember that the AsyncCommand class’s constructor takes a Func<Task> object as the first argument, representing the action to be executed by the command, and optionally a second argument of type Func<bool> that determines whether the command can be executed.

Chapter summary

The Xamarin Community Toolkit enhances your development experience with the MVVM pattern by allowing you to handle events as commands via the EventToCommandBehavior class. It also makes it easier to define asynchronous commands via the IAsyncCommand interface and the CommandFactory.Create method.

The library actually does more: it provides tools to simplify the way applications can be localized, as discussed in the next chapter.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.