Dans le développement d'applications WPF, les événements sont inévitables. Souvent, lorsqu'on ne suit pas strictement le pattern MVVM, il est courant de définir directement des événements dans le XAML et de gérer leur logique dans les fichiers .cs. Cette approche simplifie l'implémentation sans nécessiter de respecter rigoureusement les conventions de codage. Toutefois, lorsque l'on adopte le modèle MVVM, il est préférable de déplacer la gestion des événements vers la couche ViewModel pour améliorer la clarté et la structure du code.
Utilisation classique
1. Importation de l'espace de noms
Il faut importer la bibliothèque System.Windows.Interactivity.dll. Cela permet d'accéder aux fonctionnalités permettant de lier les événements à des commandes dans la ViewModel. Voici comment inclure l'espace de nom :
</div>Une autre méthode équivalente est :
<div>```
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Voici un exemple utilisant les événements LostFocus et GotFocus d'un contrôle TextBox.
</div>La classe `InvokeCommandAction` définit les propriétés suivantes :
<div>```
namespace System.Windows.Interactivity
{
public sealed class InvokeCommandAction : TriggerAction<DependencyObject>
{
public static readonly DependencyProperty CommandProperty;
public static readonly DependencyProperty CommandParameterProperty;
public InvokeCommandAction();
public string CommandName { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
protected override void Invoke(object parameter);
}
}
2.1 Définition d’un paramètre étendu
/// <summary>
/// Paramètres de l'événement
/// </summary>
public EventArgs EventArgs { get; set; }
/// <summary>
/// Paramètre supplémentaire
/// </summary>
public object Parameter { get; set; }
}
</div>**2.2 Implémentation personnalisée de InvokeCommandAction**
<div>```
public class ExtendedInvokeCommandAction : TriggerAction<DependencyObject>
{
private string commandName;
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExtendedInvokeCommandAction), null);
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExtendedInvokeCommandAction), null);
public string CommandName
{
get
{
base.ReadPreamble();
return this.commandName;
}
set
{
if (this.CommandName != value)
{
base.WritePreamble();
this.commandName = value;
base.WritePostscript();
}
}
}
public ICommand Command
{
get { return (ICommand)base.GetValue(ExtendedInvokeCommandAction.CommandProperty); }
set { base.SetValue(ExtendedInvokeCommandAction.CommandProperty, value); }
}
public object CommandParameter
{
get { return base.GetValue(ExtendedInvokeCommandAction.CommandParameterProperty); }
set { base.SetValue(ExtendedInvokeCommandAction.CommandParameterProperty, value); }
}
protected override void Invoke(object parameter)
{
if (base.AssociatedObject != null)
{
ICommand command = this.ResolveCommand();
ExtendedCommandParameter extendedParam = new ExtendedCommandParameter
{
Sender = base.AssociatedObject,
Parameter = GetValue(CommandParameterProperty),
EventArgs = parameter as EventArgs
};
if (command != null && command.CanExecute(extendedParam))
{
command.Execute(extendedParam);
}
}
}
private ICommand ResolveCommand()
{
ICommand result = null;
if (this.Command != null)
{
result = this.Command;
}
else
{
if (base.AssociatedObject != null)
{
Type type = base.AssociatedObject.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo propertyInfo in properties)
{
if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal))
{
result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);
}
}
}
}
return result;
}
}
</div>Asurez-vous d'avoir importé l'espace de noms personnalisé (`interactive`) avant de l'utiliser. Le type de `EventArgs` varie selon l'événement : par exemple, `RoutedEventArgs` pour `Loaded`, ou `SelectionChangedEventArgs` pour `SelectionChanged`.
**3. Extension via Behavior**
Le composant `System.Windows.Interactivity.dll` propose également des extensions via `Behavior`. Voici un exemple appliquant un effet d'ombre portée sur des contrôles `TextBlock` et `Button`.
**Implémentation de Behavior**
<div>```
public class EffectBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
}
private void AssociatedObject_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
var element = sender as FrameworkElement;
element.Effect = new DropShadowEffect() { Color = Colors.Transparent, ShadowDepth = 2 };
}
private void AssociatedObject_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
var element = sender as FrameworkElement;
element.Effect = new DropShadowEffect() { Color = Colors.Red, ShadowDepth = 2 };
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
}
}
</div></div>