Dans ce Tutorial nous découvrirons deux versions, un qui fonctionne sans paramètre et l'autre avec paramètre
using System;
using System.Reflection;
using System.Windows.Input;
using Microsoft.Maui.Controls;
namespace MauiApp1
{
public class EventToCommandBehavior : Behavior<VisualElement>
{
public static readonly BindableProperty EventNameProperty = BindableProperty.Create(
nameof(EventName), typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create(
nameof(Command), typeof(ICommand), typeof(EventToCommandBehavior));
public string EventName
{
get => (string)GetValue(EventNameProperty);
set => SetValue(EventNameProperty, value);
}
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
private Delegate _eventHandler;
private VisualElement _associatedObject;
public VisualElement AssociatedObject
{
get => _associatedObject;
private set => _associatedObject = value;
}
protected override void OnAttachedTo(VisualElement bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
AttachEvent(bindable);
bindable.BindingContextChanged += OnBindingContextChanged;
BindingContext = bindable.BindingContext;
}
protected override void OnDetachingFrom(VisualElement bindable)
{
DetachEvent(bindable);
AssociatedObject = null;
bindable.BindingContextChanged -= OnBindingContextChanged;
base.OnDetachingFrom(bindable);
}
private async void OnBindingContextChanged(object sender, EventArgs e)
{
await Task.Delay(100); // Ajoutez un léger délai pour la synchronisation
BindingContext = ((BindableObject)sender).BindingContext;
}
private void AttachEvent(VisualElement bindable)
{
if (string.IsNullOrWhiteSpace(EventName))
return;
var eventInfo = bindable.GetType().GetEvent(EventName);
if (eventInfo == null)
{
throw new ArgumentException($"No event named '{EventName}' found on type '{bindable.GetType().Name}'");
}
var methodInfo = typeof(EventToCommandBehavior).GetMethod(nameof(OnEventFired), BindingFlags.Instance | BindingFlags.NonPublic);
_eventHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
eventInfo.AddEventHandler(bindable, _eventHandler);
}
private void DetachEvent(VisualElement bindable)
{
if (_eventHandler == null)
return;
var eventInfo = bindable.GetType().GetEvent(EventName);
if (eventInfo != null)
{
eventInfo.RemoveEventHandler(bindable, _eventHandler);
}
_eventHandler = null;
}
private void OnEventFired(object sender, EventArgs eventArgs)
{
if (Command == null)
return;
if (Command.CanExecute(eventArgs))
{
Command.Execute(eventArgs);
}
}
private static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
return;
behavior.DetachEvent(behavior.AssociatedObject);
behavior.AttachEvent(behavior.AssociatedObject);
}
}
} public class EventToCommandBehaviorWithParameter : EventToCommandBehavior
{
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
nameof(CommandParameter), typeof(object), typeof(EventToCommandBehaviorWithParameter), null);
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
// On override la méthode de gestion d'événement
private new void OnEventFired(object sender, EventArgs eventArgs)
{
var parameter = CommandParameter ?? eventArgs;
if (Command?.CanExecute(parameter) == true)
Command.Execute(parameter);
}
// On override AttachEvent pour brancher la méthode locale
protected override void AttachEvent(VisualElement bindable)
{
if (string.IsNullOrWhiteSpace(EventName))
return;
var eventInfo = bindable.GetType().GetEvent(EventName);
if (eventInfo == null)
throw new ArgumentException($"No event named '{EventName}' found on type '{bindable.GetType().Name}'");
var methodInfo = typeof(EventToCommandBehaviorWithParameter).GetMethod(nameof(OnEventFired), BindingFlags.Instance | BindingFlags.NonPublic);
var handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
eventInfo.AddEventHandler(bindable, handler);
}
}Ajoutez la classe MainViewModel au projet.
using System.Windows.Input;
namespace MauiApp1
{
public class MainViewModel
{
public ICommand UnfocusedCommand { get; }
public MainViewModel()
{
UnfocusedCommand = new Command(OnUnfocused);
}
private void OnUnfocused(object obj)
{
if (obj is Entry entry)
{
App.Current.MainPage.DisplayAlert("Test de behavior", entry.Text, "OK");
}
else App.Current.MainPage.DisplayAlert("Test de behavior", "Behavior testé avec succès", "OK");
}
}
}<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp1"
x:Class="MauiApp1.MainPage">
<ContentPage.BindingContext>
<local:MainViewModel />
</ContentPage.BindingContext>
<StackLayout>
<Entry Placeholder="Tapez ici...">
<Entry.Behaviors>
<local:EventToCommandBehavior EventName="Unfocused"
Command="{Binding UnfocusedCommand}" />
</Entry.Behaviors>
</Entry>
<Entry Placeholder="Et puis là ..." />
</StackLayout>
</ContentPage><StackLayout>
<Entry Placeholder="Tapez ici..." x:Name="entry">
<Entry.Behaviors>
<local:EventToCommandBehaviorWithParameter EventName="Unfocused"
Command="{Binding UnfocusedCommand}"
CommandParameter="{Binding Source={x:Reference entry}}"
/>
</Entry.Behaviors>
</Entry>
<Entry Placeholder="Et puis là ..." />
</StackLayout>
Remarque:
Le léger délai dans la méthode OnBindingContextChanged peut aider à résoudre les problèmes de synchronisation liés aux liaisons de données. Cela donne au système le temps de mettre à jour les liaisons avant que le comportement ne tente d'exécuter la commande. Si le problème persiste, il peut être utile d'examiner d'autres parties de l'application pour des problèmes potentiels de performance ou de synchronisation.