This article is part of the MVVM – Made Easy series, focusing on handling UI events with ViewModel commands.
This series comprises articles that provide insights into working with the MVVM design pattern, to make it a delightful experience. All the articles in this series can be accessed from here – MVVM – Made Easy
The View serves as the user interface, while the ViewModel is composed of Properties and Commands that encapsulate the underlying logic. This architectural layering offers the distinct advantage of enabling independent development and testing of both components. Data Binding functions as the cohesive element that interlinks these two layers.
An event represents a significant mechanism for notifying state changes, such as a button click, to other components that have a vested interest in the occurrence.
The receiver component is capable of responding to the event by subscribing to it through a method whose signature corresponds with the delegate of the event ensuring type safety. And that method is generally referred to as an event handler.
However, the challenge emerges during the testing of the ViewModel, as the event is closely intertwined with the View. Business logic can’t be independently tested.
Solution:
Fortunately, Behaviors provides a solution within .NET MAUI. As the term indicates, when defined, Behaviors modify the functionality of the View by being attached to it. They can be detached at any moment, thereby removing that functionality. Multiple behaviors can be associated with the View.
In .NET MAUI, Behaviors are defined at the VisualElement level. The foundational type pertains to an element that has a visual appearance and can receive touch input. In essence, this corresponds to the UI control.
The stage is set, and now we have the UI (the View), ViewModel Command, and an option to modify the functionality of the View. So it’s appropriate to introduce EventToCommandBehavior from the .NET MAUI CommunityToolkit.
This toolkit comprises a comprehensive collection of essential components, including Alerts, Behaviors, Converters, Extensions, Views, and others, which are vital for the development of real-world apps.
With the release of v10.0 of the .NET MAUI Community Toolkit (exclusive to .NET 9), a significant breaking change has emerged that impacts the functionality of Behaviors. This issue has been thoroughly discussed, and the resolution can be found here.
This EventToCommandBehavior exemplifies one of the most beneficial types within the MVVM design, serving as the standard resolution in contexts wherein Commands are not defined by the View and must depend on Events for state changes.
It is characterized by the following properties (all of them are bindable):
- Command
- The Command that should be executed
- CommandParameter
- An optional parameter to forward to the Command.
- EventName
- The name of the Event that should be associated with the Command.
- EventArgsConverter
- An optional IValueConverter that can be used to convert EventArgs values as a parameter passed into the Command.
Usage:
It is as simple as that; one only needs to incorporate this behavior into the View and define both the Command and EventName.
If the event argument properties are essential for their handling, it is incumbent upon one to define the EventArgsConverter to pass them to the Command as its parameter. Any custom value may also be conveyed directly via the CommandParameter property.
In the below sample code, the incoming value is available within the event argument property titled Message. So the converter does the trick.
Generic Type:
public class EventToCommandBehavior<TType> : EventToCommandBehavior {}
There exists a generic variant of this EventToCommandBehavior type, with the EventArgs type as its generic type parameter. The advantage of this variant over the non-generic one is that it does not necessitate the use of a converter, as it transmits the strongly typed EventArgs object as the Command parameter.
Since the EventArgs type is intrinsically linked to the UI Framework, this generic variant may not be the most suitable choice if your ViewModels are shared across various AppModels.
Pro Tip:
One of the members of the .NET MAUI Developers group on LinkedIn posed an insightful inquiry regarding the prevention of Command execution bound to this Event under specific circumstances, such as awaiting the completion of Initialization (as events can be unsubscribed).
The response to that is Commands are equipped with the CanExecute feature. When defined, execution will occur only if that condition evaluates to true; otherwise, it will be bypassed. It is essential to define and include the logic within the CanExecute. The sample has been updated to reflect this adjustment. Further info regarding Commands can be found in one of the MVVM articles in this series, linked here.
The RelayCommand within the MVVM Toolkit facilitates the specification of the CanExecute condition as either a method or a property, with the sole stipulation being that it must yield a boolean value.
Code Sample:
ViewModel:
namespace MyApp.ViewModels;
public partial class MainViewModel : ObservableObject
{
// Ideally this should be in BaseViewModel
public bool InitComplete { get; protected set; }
// CanExecute can be a method/property that returns a boolean value.
[RelayCommand(CanExecute = nameof(CanShowMessage))]
private void ShowMessage(string message)
{
// Process the message
}
// For explanation, adding this initialization condition
private bool CanShowMessage() => InitComplete;
}
XAML:
<ContentPage
x:Class="MyApp.Views.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:c="clr-namespace:MyApp.Converters"
xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:DataType="vm:MainViewModel">
<ContentPage.Resources>
<c:HybridWebViewRawMessageReceivedEventArgsConverter x:Key="ArgsConverter" />
</ContentPage.Resources>
<HybridWebView>
<HybridWebView.Behaviors>
<mct:EventToCommandBehavior
Command="{Binding ShowMessageCommand}"
EventArgsConverter="{StaticResource ArgsConverter}"
EventName="RawMessageReceived" />
</HybridWebView.Behaviors>
</HybridWebView>
</ContentPage>
C#:
using MyApp.Converters;
using MyApp.ViewModels;
using CommunityToolkit.Maui.Behaviors;
using CommunityToolkit.Maui.Markup;
namespace MyApp.Views;
public class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new MainViewModel();
Resources.Add("ArgsConverter", new HybridWebViewRawMessageReceivedEventArgsConverter());
Content = new HybridWebView()
{
Behaviors =
{
new EventToCommandBehavior()
{
EventArgsConverter = (IValueConverter)Resources["ArgsConverter"],
EventName = nameof(HybridWebView.RawMessageReceived),
}.Bind(EventToCommandBehavior.CommandProperty, static (MainViewModel vm) => vm.ShowMessageCommand),
},
};
}
}
HybridWebView:
This is a new control released in .NET MAUI 9 for developing JavaScript-based Hybrid Apps. Consult this Exploring HybridWebView article to delve into the details of this new control.
To know more about what’s new in .NET MAUI 9, refer to this Comprehensive Overview article.
Conclusion:
The implementation of the EventToCommandBehavior facilitates the independent development and testing of both Views and ViewModels. This methodology enables a greater degree of code sharing.
Conceptually, this can be applied to all XAML-based frameworks, but due to the distinct workings of each AppModel, they were not included in the article. Generally, it is implemented using a Behavior or a Trigger.
Further Reading:
Happy coding. Stay connected as we continue to learn and share the experiences from this exciting journey of being a .NET developer.
One reply on “Handling UI Events with MVVM Commands in .NET MAUI using EventToCommandBehavior”
[…] Handling UI Events with MVVM Commands in .NET MAUI (Vijay Anand E G) […]