Juanma Santoyo

En ocasiones me llaman friki

Reaccionar a los objetos etiquetados en Microsoft Surface: el control TagVisualization.

| 4 Comentarios

Hace unos meses, hablaba por aquí sobre los contactos en pantalla (Detección y gestión de contactos en una Surface).

Aunque hay otra forma de reaccionar a los contactos etiquetados: En Surface es muy común posicionar un gráfico justo bajo el objeto etiquetado. Algo como esto:

Este gráfico debe posicionarse siempre bajo nuestro objeto y orientarse según lo haga el objeto. Además, dependiendo del tipo de etiqueta o su valor, este gráfico puede tener comportamientos distintos. Para los casos en los que queremos responder a un objeto etiquetado de esta forma, tenemos dos objetos que nos facilitarán la vida: El TagVisualizer y el TagVisualization.

El control TagVisualization.

El objeto TagVisualization nos proporciona rápidamente un control de usuario que tiene como único objetivo posicionarse bajo los elementos etiquetados que toquen la pantalla. Como todos los controles de usuario, se basa en una parte XAML, donde especificamos la parte gráfica del control de usuario, y una parte en C#, donde especificamos la funcionalidad (por ejemplo, podemos programar un método que lance una animación en el TagVisualization, o un método para mostrar un mensaje).

Los objetos TagVisualization se pueden crear desde el menú de añadir elementos de Visual Studio:

Abrir la pantalla de "Añadir nuevo elemento"

Abrir la pantalla de "Añadir nuevo elemento"

Añadir el control de usuario "Tag Visualization"

Añadir el control de usuario "Tag Visualization"

Pero en realidad, un control TagVisualization son sirve de poco sin su respectivo TagVisualizer.

El control TagVisualizer.

Si un TagVisualization es el control de usuario que se posiciona bajo un contacto etiquetado, un TagVisualizer es el control que gestiona todos los TagVisualization que tenemos. Tiene varias funciones:

  1. Determina el área de la pantalla que será sensible al posicionamiento de objetos etiquetados.
  2. Discrimina entre los diferentes tipos de etiquetas, de forma que según indiquemos, muestra un TagVisualization o otro en función de la etiqueta del objeto.
  3. Detecta los eventos de entrada y salida de los contactos. Estos eventos son especialmente útiles porque desde ellos podemos ejecutar funcionalidades del TagVisualization.

Mejor con un ejemplo.

Vamos a programar un pequeño ejemplo para ver más cláramente cómo funcionan ambos objetos. El ejemplo consistirá en una región de pantalla donde podremos posicionar un objeto etiquetado. Al posicionarlo aparecerá debajo un círculo, el cual se mostrará con una animación sencilla.

Creamos el proyecto y la pantalla principal.

Crearemos un proyecto de Surface con Visual Studio. Lo llamaremos “ContactosEtiquetados”. En lo particular, a mi me gusta cargarme la pantalla que viene por defecto y crearme la mía “Main.xaml”. Recordad que si hacéis esto, tenéis que cambiar el valor del atributo StartupUri en el archivo App.config.

En la pantalla inicial, no nos complicamos. Únicamente meteremos el TagVisualizer, y le daremos algun detalle gráfico para poder visualizarlo en pantalla:

<s:SurfaceWindow x:Class="ContactosEtiquetados.Main"
    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"
    Title="ContactosEtiquetados"
    Width="1024" Height="768"
    Loaded="SurfaceWindow_Loaded"
>
    <s:SurfaceWindow.Resources>
        <ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/MainBackground.jpg"/>
    </s:SurfaceWindow.Resources>

    <Grid Background="{StaticResource WindowBackground}" >
        <s:TagVisualizer
            x:Name="Visualizer"
            Width="700" Height="550"
            HorizontalAlignment="Center" VerticalAlignment="Center"
            BorderBrush="White" BorderThickness="2"">

        </s:TagVisualizer>
    </Grid>
</s:SurfaceWindow>

Sólo destacar que al TagVisualizer le he asignado dos eventos: VisualizationAdded y VisualizationRemoved. Efectívamente, se lanzarán cuando entre y salga un contacto respectívamente. Tambien he asignado el evento Loaded a la SurfaceWindow.

Creamos el objeto TagVisualization.

Crear un objeto TagVisualization es sencillo: el propio Visual Studio lo hace desde la ventana de “Añadir nuevo elemento” (lo hemos visto antes).

En el nuevo control introduciremos dos gráficos y una grid. Los gráficos estarán dentro de la grid, y serán un círculo grande sin relleno y otro más pequeño con relleno. Éste último lo posicionaremos sobre el contorno del más grande. Por último, añadiremos un RotateTransform a la grid que contiene ambos círculos:

<s:TagVisualization x:Class="ContactosEtiquetados.TagVisualization"
    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"
    Loaded="TagVisualization_Loaded"
    Width="300" Height="300">
    <Grid>
        <Grid>
            <Ellipse Width="200" Height="200" Stroke="Red" StrokeThickness="2" />

            <Ellipse Width="50" Height="50" Fill="Red" Margin="200,0,0,0" />

            <Grid.RenderTransform>
                <RotateTransform CenterX="150" CenterY="150" Angle="0" />
            </Grid.RenderTransform>
        </Grid>
    </Grid>
</s:TagVisualization>

Gestión de los contactos etiquetados.

La gestión de los contactos que recibe el TagVisualizer se basa en objetos TagVisualizationDefinition. A un TagVisualizer se le añaden una o varias definiciones. Cada definición asociará el TagVisualizer con un TagVisualization y además nos permitirá discriminar los contactos según el valor de la etiqueta. Lo normal es crear las definiciones que necesitemos extendiendo la clase TagVisualizationDefinition. Por ejemplo, en nuestro caso sólo reaccionaremos a los contactos de tipo Identity. Creamos la clase ContactosEtiquetadosVisualizationDefinition.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation;
using System.Windows;

namespace ContactosEtiquetados
{
    class ContactosEtiquetadosVisualizationDefinition : TagVisualizationDefinition
    {
        protected override bool Matches(TagData tag)
        {
            if (tag.Type == TagType.Identity) return true;
            else return false;
        }

        protected override Freezable CreateInstanceCore()
        {
            return new ContactosEtiquetadosVisualizationDefinition();
        }
    }
}

El método Matches es la clave para determinar qué contactos hay que tener en cuenta.

Ahora añadimos la definición al objeto TagVisualizer. Lo haremos en el evento Load de la ventana inicial:

        private void SurfaceWindow_Loaded(object sender, RoutedEventArgs e)
        {
            TagVisualizationDefinition definition = new ContactosEtiquetadosVisualizationDefinition();
            definition.Source = new Uri("TagVisualization.xaml", UriKind.Relative);
            definition.LostTagTimeout = 1000;
            Visualizer.Definitions.Add(definition);
        }

Comentamos un poco: La propiedad Source símplemente hace referencia al XAML del objeto TagVisualization. La propiead LostTagTimeOut define un tiempo de espera antes de “matar” al TagVisualization, en caso de que el contacto deje de estar en contacto con la pantalla. Dicho de otra forma, el TagVisualization no desaparecería hasta que el contacto no lleve más de un segundo perdido.

Programamos la animación del TagVisualization.

Para animar el control TagVisualization rescataremos un artículo que escribí sobre el tema hace un tiempo: Animar transformaciones en WPF con el método BeginAnimation. Aplicar la animación al TagVisualization es simple:

Primero, vamos a crear un método en el TagVisualization que inicie la animación:

private void StartAnimation()
{
    DoubleAnimation animacion = new DoubleAnimation();

    animacion.To = 360;
    animacion.Duration = new Duration(TimeSpan.FromMilliseconds(1000));

    // La animaci&Atilde;&sup3;n se repite continuamente
    animacion.RepeatBehavior = RepeatBehavior.Forever;

    this.Rotacion.BeginAnimation(RotateTransform.AngleProperty, animacion);
}

Ésta animación puede ser iniciada en el evento Loaded:

private void Visualization_Loaded(object sender, RoutedEventArgs e)
{
    this.StartAnimation();
}

¡Hemos acabado!

Con ésto ya podemos programar muchas respuestas interesantes a nuestros contactos etiquetados.

El ejemplo descargable lo tenéis aquí: Contactos etiquetados.

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

4 Comments

  1. Marca error el ejemplo cuando se ejecuta.

    • ¡Vaya! ¿Qué tipo de error?

      Lo cierto es que este ejemplo corre sobre un SDK bastante antiguo ya, quizás tu problema sea que no tienes el entorno configurado correctamente (debe ser un entorno para surface 1, que no creo que sea sencillo de montar hoy en día).

      Saludos.

      • Si, a lo mejor es eso, tengo el Surface 2, muchas gracias por tu respuesta.

        • Bueno, el código de Surface 1 jamás funcionará bien en un SDK de Surface 2, ya que no tienen nada que ver el uno con el otro (ambos están montados sobre bases diferentes).

          Si te sirve de algo, recuerdo que Microsoft creó una herramienta para portar el código de un SDK al otro.

          Saludos.

Deja un comentario

Los campos obligatorios están marcados con *.