Juanma Santoyo

En ocasiones me llaman friki

Crear eventos propios en aplicaciones de Surface

| No hay comentarios

En las aplicaciones de Surface, los eventos son básicos.

La mayoría de las veces, nos bastará con los eventos que nos proporciona el SDK, pero en ocasiones necesitamos algo más específico, o incluso reaccionar a una combinación de eventos. Necesitamos eventos propios: poder disparar el evento cuando queramos, pasar la información que queramos; y que otra clase que esté escuchando el evento realice una acción.

De eso hablaré en este artículo. Comentaré un pequeño ejemplo donde veremos cómo podemos implementar un evento de doble tap (lo llamaremos DoubleTap). Este evento no viene implementado en el SDK de Surface, y como análogo al por todos conocido “Doble Click” de los PC, puede resultar muy útil.

Implementación de un evento DoubleTap.

Vamos a organizarnos un poco: ¿cómo vamos a implementar el DoubleTap?. Pues bien, en esencia un DoubleTap no es más que dos ContactDown en un periodo corto de tiempo. El ContactDown es un evento que sí que viene implementado en el SDK, así que sólo necesitamos almacenar el instante en el que se realizó el primer ContactDown para comprobar si el tiempo entre dos ContactDown es lo suficientemente corto. De serlo, se dispararía el DoubleTap. Es decir, que necesitamos:

  • Un manejador para el evento ContactDown.
  • Una propiedad global de tipo DateTime que almacenará el momento del último ContactDown.

Todo esto lo programaríamos dentro del objeto al que queremos dotar de la capacidad de lanzar DoubleTap. En nuestro caso, será un control de usuario (lo normal).

Programamos el control de usuario.

Gráficamente lo haremos fácil: un rectángulo. Este es el XAML:

<s:SurfaceUserControl x:Class="ProgramarEventosPropios.DoubleTapUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008">
    <Grid>
        <Rectangle Fill="Red" Width="100" Height="100" />
    </Grid>
</s:SurfaceUserControl>

Y ahora, el C#. Aquí es donde programaremos el evento DoubleTap, pero primero, vayamos a lo principal: gestionar el evento ContactDown y el tiempo entre dos ContactDown:

DateTime LastContactDown = DateTime.MinValue;
TimeSpan MaxMilis = TimeSpan.FromMilliseconds(700);

protected override void OnContactDown(ContactEventArgs e)
{
    base.OnContactDown(e);

    if (this.LastContactDown == DateTime.MinValue)
    {
        this.LastContactDown = DateTime.Now;
    }
    else
    {
        TimeSpan time = DateTime.Now - this.LastContactDown;

         if (time <= this.MaxMilis)
         {
             RaiseDoubleTapEvent(time);
         }

         this.LastContactDown = DateTime.MinValue;
    }
}

Varias cosas a comentar de este código:

  1. La variable MaxMilis sólo define el tiempo máximo entre ContactDown. Únicamente cuando el tiempo es inferior o igual, se considera un DoubleTap.
  2. Por otra parte, mirad cómo asigno el evento ContactDown. Lo normal es asignar un manejador, pero en este caso hago una sobreescritura del método OnContactDown. Los eventos también se pueden asignar así, aunque sinceramente, yo prefiero añadir un manejador en el XAML (en esta ocasión lo hago con sobreescritura a modo ilustrativo).
  3. Por último, fijaos que en caso de cumplirse el DoubleTap, ejecuto el método RaiseDoubleTapEvent con el parámetro time. El método lo veremos ahora, se trata del disparador del evento. Los disparadores no son necesarios, pero sí muy recomendables. El parámetro time lo paso por que es la información que quiero enviar con el evento (en un objeto EventArgs). De esta forma, los manejadores del evento DoubleTap pueden saber cuánto tiempo pasó entre los dos ContactDown (no es una información demasiado útil, pero para ilustrar como funcionan los objetos EventArgs nos viene de perlas).

Y ahora empieza lo bueno: programamos el evento.

En realidad, hay que programar varias cosas:

  1. El evento.
  2. Las gestión de los manejadores que quieran escuchar el evento.
  3. Un disparador para el evento.

Programamos el evento.

En realidad lo único complejo es el registro del evento. Y no es que sea complejo, es sólo que es muy configurable (y requiere muchos parámetros).

public delegate void DoubleTapEventHandler(object sender, DoubleTapEventArgs e);

public static readonly RoutedEvent DoubleTapEvent = EventManager.RegisterRoutedEvent(
	"DoubleTap",
	RoutingStrategy.Direct,
	typeof(DoubleTapEventHandler),
	typeof(DoubleTapUserControl));

Lo primero, definimos el manejador del evento. Le damos nombre, y; sobre todo; determinamos que los argumentos vienen como un objeto DoubleTapEventArgs. Este objeto lo crearemos luego, y tiene la particularidad de tener una propiedad para almacenar el tiempo entre los dos ContactDown ¿recordáis que al disparador le pasábamos un parámetro time?.

En cuanto al registro del evento, le pasamos cuatro parámetros:

  1. El nombre del evento.
  2. La estrategia del evento. Puede ser Direct, Tunnel o Bubble. Sirve para determinar la dirección de propagación. En este caso, Direct no se propaga.
  3. El tipo del manejador. No es casualidad que coincida con lo declarado en la línea 1.
  4. El tipo del objeto que dispara el evento. En este caso, el nombre de nuestro control de usuario.

La gestión de manejadores.

Esto se parece a los típicos getters / setters de .Net. Se trata simplemente de gestionar lo que ocurre cuando se añaden o se quitan manejadores de los eventos.

public event DoubleTapEventHandler DoubleTap
{
    add
    {
        AddHandler(DoubleTapEvent, value);
    }
    remove
    {
        RemoveHandler(DoubleTapEvent, value);
    }
}

El disparador.

Este método se encarga de lanzar el evento. Un disparador debería hacer tres cosas:

  1. Recibir por parámetro los valores que hay que enviar como argumentos del evento.
  2. Montar un objeto EventArgs que contenga los valores a enviar.
  3. Lanzar el evento.

Es muy sencillo:

private void RaiseDoubleTapEvent(TimeSpan TimeBetweenTaps)
{
    DoubleTapEventArgs Args = new DoubleTapEventArgs(TimeBetweenTaps);
    Args.RoutedEvent = DoubleTapUserControl.DoubleTapEvent;

    RaiseEvent(Args);
}

Y este sería el objeto DoubleTapEventArgs. La idea es que es una extensión del objeto RoutedEventArgs.

public class DoubleTapEventArgs : RoutedEventArgs
{
	TimeSpan timeBetweenTaps = TimeSpan.MinValue;
	public TimeSpan TimeBetweenTaps
	{
	  get { return timeBetweenTaps; }
	}

	public DoubleTapEventArgs(TimeSpan TimeBetweenTaps) : base()
	{
		this.timeBetweenTaps = TimeBetweenTaps;
	}
}

Con esto ya hemos programado un control de usuario capaz de detectar y disparar un evento DoubleTap. ¿Cómo lo usamos?. Vamos a añadir este objeto a la ventana inicial de la aplicación y lo vemos. En realidad, como si fuese un evento normal y corriente.

Este es el XAML de la ventana:

<s:SurfaceWindow x:Class="ProgramarEventosPropios.SurfaceWindow1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    xmlns:self="clr-namespace:ProgramarEventosPropios"
    Title="ProgramarEventosPropios"
>
    <s:SurfaceWindow.Resources>
        <ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/>
    </s:SurfaceWindow.Resources>

    <Grid Background="{StaticResource WindowBackground}" >
        <self:DoubleTapUserControl x:Name="DoubleTapUC" DoubleTap="DoubleTapUC_DoubleTap" />
    </Grid>

Y desde el C# programamos el manejador del evento:

void DoubleTapUC_DoubleTap(object sender, DoubleTapEventArgs e)
{
    Debug.WriteLine("Double Tap. Tiempo: {0}", e.TimeBetweenTaps.TotalMilliseconds);
}

Descarga del ejemplo.

Y como no podía ser de otra forma, os dejo aquí la descarga a un pequeño proyecto que es el que programé para redactar este artículo.

Crear eventos propios.

¡Hemos acabado!.

Con esto ya tenemos un evento propio funcionando. Este tipo de eventos nos serán muy útiles en nuestras aplicaciones, y nos solucionarán la vida en muchas ocasiones. No está de más decir que realmente este tipo de eventos se pueden implementar en cualquier aplicación WPF, no sólo en las aplicaciones de Surface.

Espero que hayáis encontrado útil este artículo.

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

Deja un comentario

Los campos obligatorios están marcados con *.