Monday, 1 August 2022

Behaviors library for .NET MAUI

Many years ago I wrote a behaviours library for Xamarin.Forms. The conventional view was that behaviours extend the functionality of controls, with typical examples validating user text input. Inspired by the then Blend SDK, my thought was that behaviors can be split into two concepts. Behaviours are attached to a control and listen for something to happen. When the something happens, it triggers one or more actions in response. So actions are invoked by behaviours and executed on a specified control. Typical behaviors are listening for an event firing, or data changing. Typical actions are invoking a command, invoking a method, setting a property etc.

I ended up producing a behaviours library for Xamarin.Forms, based on the Blend SDK, that I shipped on NuGet and for a while it was moderately successful. I updated it from time to time, but eventually forgot all about it.

I’ve now resurrected it for .NET MAUI. But I’m not going to ship it as a NuGet. Doing so for the previous version caused me lots of work that I’d like to avoid now.

What is it?

Behaviours for .NET MAUI is a class library I’ve created that can be consumed by .NET MAUI apps. It supports the following scenarios:

  • Invoking commands from XAML when an event fires or when data changes.
  • Invoking methods from XAML (in the view or view model) when an event fires or when data changes.
  • Setting properties from XAML (in the view or view model) when an event fires or when data changes.
  • Invoke animations from XAML, including compound animations, when an event fires or when data changes.
  • Triggering a specified VisualState on a VisualElement from XAML, when an event fires or when data changes.

The result of using the library is that you can eliminate lots of boiler plate C# code, instead moving it to XAML.

Behaviours

The library contains the following behaviours:

  • EventHandlerBehavior - listens for a specific event to occur, and executes one or more actions in response.
  • DataChangedBehavior - listens for the bound data to meet a specified condition, and executes one or more actions in response.

Actions

The library contains the following actions:

  • InvokeCommandAction - executes a specified ICommand when invoked.
  • InvokeMethodAction - executes a method on a specified object when invoked.
  • SetPropertyAction - changes a specified property to a specified value.
  • FadeAction - performs a fade animation when invoked,
  • RotateAction - performs a rotate animation when invoked.
  • ScaleAction - performs a scale animation when invoked.
  • TranslateAction - performs a translate animation when invoked.
  • GoToStateAction - invokes visual state changes.

Where is it?

You can download the library and a sample that demos it from its repo.

How do I use it?

Using the library is a three step process:

  1. Clone the library from its repo and add the BehaviorsLibrary class library project to your .NET MAUI solution.
  2. Add a reference to the BehaviorsLibrary project to your app project.
  3. Add an xmlns to the library to your XAML file, and then consume the required behaviors/actions from XAML.

The following code example shows an example of using the EventHandlerBehavior to invoke two commands:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
             ...>

    <ContentPage.Resources>
        <converters:SelectedItemEventArgsToSelectedItemConverter x:Key="SelectedItemConverter" />
    </ContentPage.Resources>
	...
    <ListView x:Name="listView"
                 ItemsSource="{Binding People}">
        <ListView.Behaviors>
            <behaviors:EventHandlerBehavior EventName="ItemSelected">
                <behaviors:InvokeCommandAction Command="{Binding ItemSelectedCommand}"
                                               Converter="{StaticResource SelectedItemConverter}" />
                <behaviors:InvokeCommandAction Command="{Binding OutputAgeCommand}"
                                               Converter="{StaticResource SelectedItemConverter}"
                                               ConverterParameter="35" />
            </behaviors:EventHandlerBehavior>
        </ListView.Behaviors>
    </ListView>
</ContentPage>

In this example, when the ListView.ItemSelected event is raised, the ItemSelectedCommand and OutputAgeCommand are sequentially executed on the bound view model (the InvokeCommandAction class expects to find the Command objects on the BindingContext of the attached object). The advantage of this approach is that it enables commands to be associated with controls that weren’t designed to interact with commands, thereby removing boiler-plate event handling code from code-behind files.

In the coming weeks I’ll explore all the functionality the library offers in more detail.