Enlazar comandos a cualquier evento en MVVM

- 5 minutos de lectura

En la primera entrada dedicada a MVVM vimos que en este patr贸n no utilizamos eventos, en su lugar nos valemos de los comandos para asociar una acci贸n con un determinado control. El problema es que los controles WPF no tienen propiedades para enlazar todos los eventos con comandos. Por ejemplo, en un bot贸n podemos utilizar la propiedad Command para ejecutar una acci贸n al pulsar el bot贸n. Pero, 驴qu茅 sucede si queremos asignar un comando a otro evento? 驴C贸mo hacemos, por ejemplo, para ejecutar un comando al pasar por encima de un control? 驴Y al escribir en un TextBox? 驴Y al cargar una vista?

En esta entrada vamos a ver dos m茅todos para responder a estas preguntas: el primero es mediante la implementaci贸n del patr贸n Attached Behavior y el segundo es haciendo uso de los comportamientos que nos proporcionan las clases de System.Windows.Interactivity.

Patr贸n Attached Behavior

B谩sicamente el patr贸n Attached Behavior consiste en una clase est谩tica en la que se definen una o varias propiedades asociadas (attached properties), un tipo especial de propiedad de dependencia (dependency property). En nuestra soluci贸n de ejemplo vamos a definir la clase MouseBehavior dentro del namespace BasicMVVM.Behaviors y dentro de esta clase definiremos la propiedad asociada MouseEnter. A diferencia de las dependency properties, no definimos un wrapper para las propiedades sino que tenemos que crear el par de m茅todos est谩ticos GetMouseEnter y SetMouseEnter. En el m茅todo controlador MouseEnterCallback, que se llamar谩 cada vez se cambie la propiedad, asignamos al evento MouseEnter el c贸digo para ejecutar el comando. La implementaci贸n completa de la clase queda de la siguiente forma:

namespace BasicMVVM.Behaviors 
{ 
  public static class MouseBehavior 
  { 
    public static readonly DependencyProperty MouseEnterProperty;

    static MouseBehavior()
    {
      MouseEnterProperty = DependencyProperty.RegisterAttached("MouseEnter",
      typeof(ICommand), typeof(MouseBehavior), new PropertyMetadata(MouseEnterCallback));
    }
    
    public static ICommand GetMouseEnter(UIElement element)
    {
      return (ICommand)element.GetValue(MouseEnterProperty);
    }
    
    public static void SetMouseEnter(UIElement element, ICommand value)
    {
      element.SetValue(MouseEnterProperty, value);
    }
    
    private static void MouseEnterCallback(object obj, DependencyPropertyChangedEventArgs e)
    {
      var element = obj as UIElement;
    
      element.MouseEnter += (sender, ev) =>
      {
        var el = sender as UIElement;
        var command = GetMouseEnter(el);
    
        if (command.CanExecute(null))
        {
          command.Execute(el);
        }
      };
    }   
  } 
}

Para poder utilizar este comportamiento en la vista, que en nuestro ejemplo es la vista Basic, solo tenemos que declarar el espacio de nombres BasicMVVM.Behaviors en la etiqueta Window.

<Window x:Class="BasicMVVM.Views.Basic"
...
xmlns:b="clr-namespace:BasicMVVM.Behaviors"
...>

Y para comprobar el funcionamiento, a帽adimos un nuevo bot贸n al que enlazamos el comando deseado, en este caso que el comando ChangeColorCommand.

<Button Content="Attached behavior" b:MouseBehavior.MouseEnter="{Binding ChangeColorCommand}" />

Esta soluci贸n puede llegar a ser bastante tediosa porque tenemos que implementar todas las clases con los comportamientos que queremos controlar. As铆 que una alternativa a este m茅todo es utilizar los comportamientos incluidos en las clases de System.Windows.Interactivity que veremos a continuaci贸n.

Comportamientos en System.Windows.Interactivity

System.Windows.Interactivity.dll es un ensamblado que tenemos disponible a trav茅s del SDK de Microsoft Expression Blend, y que podemos descargar en el Centro de descargas de Microsoft. Esencialmente este ensamblado lo que intenta es formalizar de alguna forma el patr贸n attached behavior que hemos visto en el punto anterior.

Una vez hemos instalado el SDK, podemos utilizar el ensamblado a帽adiendo en el proyecto la referencia a System.Windows.Interactivity.dll que se encuentra en la carpeta {Program Files}Microsoft SDKsExpressionBlend.NETFrameworkv4.0Libraries y declarando el espacio de nombres correspondiente en la vista.

<Window x:Class="BasicMVVM.Views.Basic"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...>

Para a帽adir el mismo comportamiento que en el primer ejemplo anterior a帽adimos el bot贸n con el siguiente c贸digo.

<Button Content="System.Windows.Interactivity">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseEnter">
      <i:InvokeCommandAction Command="{Binding ChangeColorCommand}"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

En este c贸digo definimos un desencadenador (trigger) que est谩 escuchando el evento MouseEnter y que al activarse invocar谩 el comando ChangeColorCommand. Podemos definir estos desencadenadores en cualquier elemento. Por ejemplo, si queremos lanzar un comando cuando la vista se haya cargado, podemos utilizar el siguiente c贸digo XAML:

<Window x:Class="BasicMVVM.Views.Basic"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">

<Window.DataContext>
<vm:Basic />
</Window.DataContext>

<i:Interaction.Triggers>
  <i:EventTrigger EventName="Loaded">
    <i:InvokeCommandAction Command="{Binding ShowWelcomeMsgCommand}"/>
  </i:EventTrigger>
</i:Interaction.Triggers>
...
</Window>

Enlaces relacionados
Descarga Microsoft Expression Blend Software Development Kit (SDK) for .NET 4
Informaci贸n general sobre propiedades asociadas
Espacio de nombres System.Windows.Interactivity

Descarga c贸digo fuente:
BasicMVVM-Behaviors.zip