Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Silverlight e la gestione del 'multi-touch'

Creare interfacce capaci di interpretare le gesture su dispositivi 'touch'
Creare interfacce capaci di interpretare le gesture su dispositivi 'touch'
Link copiato negli appunti

Non c'è che dire, è un periodo eccezionale per sviluppatori e appassionati di nuove tecnologie. È il momento delle interfacce utente "natural", ossia quelle interfacce che permettono una diretta interazione dell'utente mediante multi-touch, riconoscimento vocale o del movimento, tecnologie ormai disponibili all'utente finale ad un prezzo decisamente accettabile.

A tutti forse sono familiari quelle scene del film Minority Report nelle quali il protagonista si divertiva a spostare immagini e informazioni sullo schermo di un calcolatore mediante il semplice movimento delle mani. All'epoca sembrava proprio fantascienza, oggi basta recarsi in un qualsiasi centro commerciale, acquistare un personal computer HP Touchsmart e il gioco è fatto. Insomma: benvenuti nel futuro!

Eppure gli strumenti disponibili al programmatore, fino a qualche mese fa, non erano sufficienti per l'implementazione di "user experience" sofisticate. Il rilascio di Windows 7 ha sicuramente segnato una tappa importante integrando una serie di funzionalità dedicate all'interfacciamento con sensori esterni mediante la Windows Sensor And Location platform e un nuovo insieme di Touch API che permettono lo sviluppo di codice dedicato al controllo e interpretazione di azioni multi-touch, tecnicamente denominate gestures, generate dalla diretta azione dell'utente sullo schermo del personal computer.

Queste azioni possono essere semplici, come ad esempio il "toccare" un oggetto sullo schermo e trascinarlo senza usare il mouse, fino ad arrivare a comportamenti più complessi: il ruotare o ridimensionare componenti visuali presenti nell'applicazione con l'azione di due o più dita. Ce n'è per tutti i gusti: la lista completa delle gesture utilizzabili in Windows 7 è disponibile nella sezione MSDN Windows Touch Gestures Overview (figura 1).

Figura 1: Elenco delle gesture disponibili in Windows 7 (fonte)
(clic per ingrandire)


Elenco delle gestures disponibili in Windows 7

Per poter impiegare tali funzionalità è necessario disporre di un personal computer dotato di uno schermo abilitato per il multi-touch, come, per citarne due tra i tanti, i modelli HP della serie TouchSmart oppure l'Acer Aspire 5738PG. Nel caso non si disponesse di un hardware touch-enabled, è possibile utilizzare per i test il progetto multi-touch Vista disponibile su Codeplex.

Multi-touch da surface a Silverlight

Le funzionalità presenti in Silverlight sono derivate direttamente dalle API presenti nel progetto Surface e dall'implementazione di un sottoinsieme delle stesse presente in WPF (Windows Presentation Foundation), il sottosistema grafico incluso in alcune versioni di Windows.

La derivazione non implica per forza la presenza di tutte le caratteristiche che, viste le dimensioni del plug-in Silverlight, sono attualmente ridotte in numero e funzionalità.

La prima implementazione multi-touch in Silverlight è apparsa nella versione 3 e contiene, tra gli altri, i seguenti oggetti presenti nel namespace System.Windows.Input. La classe "Touch" in grado di gestire gli eventi generati comprende:

  • Un evento Touch.FrameReported che viene generato al verificarsi della pressione sullo schermo;
  • Una classe TouchFrameEventArgs impiegata dal precedente evento in grado di fornire informazioni dettagliate sui punti di contatto (touch points) correntemente selezionati dall'utente;
  • Una classe TouchPoint in grado di fornire le coordinate (X,Y) dei touch point e la modalità di interazione dell'utente (pressione, movimento o rilascio degli stessi).

Tali oggetti sono già sufficienti per la realizzazione di qualsiasi implementazione touch, sebbene interazioni più complesse (come rotazione, ridimensionamento di oggetti, effetti di "inerzia" corrispondenti all'accelerazione/decelerazione del movimento in base a parametri prefissati) richiedono, allo stato attuale, un bel po' di lavoro aggiuntivo.

Per tali casistiche è comunque possibile impiegare framework disponibili online, come il Microsoft Surface Manipulations and Inertia Sample for Microsoft Silverlight oppure le seguenti risorse predisposte dall'autore all'interno della "Microsoft Expression Gallery" e su Codeplex:

Intercettare gli eventi Touch

Proviamo a costruire un primo esempio in grado di intercettare gli eventi touch generati e visualizzare le coordinate dei punti selezionati e la relativa action associata a ciascuno di essi. Per iniziare creiamo una nuova "Silverlight application" e aggiungiamo un controllo DataGrid per visualizzare le coordinate (X,Y) e l'Action dei Touch points correntemente selezionati:

Listato 1: MainPage.xaml

<UserControl x:Class="BasicMultiTouch.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Canvas x:Name="LayoutRoot" Background="White">
    <my:DataGrid x:Name="touchPoints_DataGrid" AutoGenerateColumns="True" />
  </Canvas>
</UserControl>

Di seguito la definizione dell'handler per l'evento Touch.FrameReported:

Listato 2: MainPage.xaml.cs

public partial class MainPage
{
  public MainPage()
  {
    InitializeComponent();
    Touch.FrameReported += Touch_FrameReported;
  }
  void Touch_FrameReported(object sender, TouchFrameEventArgs e)
  {
    touchPoints_DataGrid.ItemsSource = e.GetTouchPoints(this);
    foreach (var touchPoint in e.GetTouchPoints(this))
    {
      if (touchPoint.Action == TouchAction.Up) continue;
      var point = new Ellipse()
      {
        Width = 40,
        Height = 40,
        Fill = new SolidColorBrush(Colors.Green)
      };
      point.SetValue(Canvas.TopProperty, touchPoint.Position.Y - 64);
      point.SetValue(Canvas.LeftProperty, touchPoint.Position.X - 64);
      LayoutRoot.Children.Add(point);
    }
  }
}

La collezione e.GetTouchPoints() permette il recupero di tutti i touchPoints correntemente selezionati dall'utente. Per ciascuno di essi è possibile verificarne lo stato (action) ovvero:

Stato (Action) Descrizione
TouchAction.Down il point è stato appena selezionato, in modo simile al tradizionale evento MouseDown
TouchAction.Up il point è stato appena rilasciato
TouchAction.Move il point è in movimento ed è possibile recuperarne le coordinate mediante tp.Position.X, tp.Position.Y

Nell'esempio descritto, per ogni azione che sia una "Up" o "Move", andremo ad aggiungere alla nostra client-area un controllo "Ellipse" corrispondente al touch-point selezionato in modo da creare un'applicazione simile a Paint. Di seguito è disponibile l'output:

Figura 2: Analisi dei principali eventi della classeSystem.Windows.Input .Touch

Analisi dei principali eventi della classe System.Windows.Input .Touch

Un passo avanti: abilitare le gesture

Vediamo ora come abilitare delle gesture più complesse mediante l'implementazione di una Silverlight/Blend Behavior che permetta l'abilitazione di un effetto zoom per il controllo a cui viene applicata. Il codice è abbastanza semplice: nel caso in cui il numero di touch points selezionati sia uguale a due, verifichiamo la distanza tra i due punti e applichiamo una ScaleTransform:

Listato 3: TouchAction.Down

private void AssociatedObjectTouchDown(object sender, TouchFrameEventArgs e)
{
  //Initialize the parameters
  this._touchDown = true;
  this.AssociatedObject.Opacity = .75;
  this._tpInitialPoints = e.GetTouchPoints(this.AssociatedObject);
  //Initialize the Scale transform
  if (this._scaleTransform == null)
  {
    this._scaleTransform = new ScaleTransform();
    if ((this.AssociatedObject.RenderTransform == null) || !(this.AssociatedObject.RenderTransform is TransformGroup))
      this.AssociatedObject.RenderTransform = new TransformGroup();
    if (this.AssociatedObject.RenderTransform is TransformGroup)
      (this.AssociatedObject.RenderTransform as TransformGroup).Children.Add(this._scaleTransform);
  }
}

Listato 4: TouchAction.Move

private void AssociatedObjectTouchMove(object sender, TouchFrameEventArgs e)
{
  if (this._touchDown)
  {
    //Find the new points
    TouchPointCollection newPoints = e.GetTouchPoints(AssociatedObject.Parent as FrameworkElement);
    if (newPoints!=null && this._tpInitialPoints!=null)
      if (this._tpInitialPoints.Count == 2 && newPoints.Count == 2)
      {
        // Zoom the Control using the ScaleTransform
        double ratio = this.Distance(newPoints.First().Position, newPoints.Last().Position) /
                       this.Distance(this._tpInitialPoints.First().Position, this._tpInitialPoints.Last().Position);
        if (ratio != double.NaN)
        {
          Point midPoint = this.MidPoint(newPoints.First().Position, newPoints.Last().Position);
          this._scaleTransform.CenterX = midPoint.X; this._scaleTransform.CenterY = midPoint.Y;
          this._scaleTransform.ScaleX = ratio; this._scaleTransform.ScaleY = ratio;
        }
      }
  }
}

Listato 5: TouchAction.Up

private void AssociatedObjectTouchUp(object sender, TouchFrameEventArgs e)
{
  // turn off Zoom mode
  this._touchDown = false;
  this.AssociatedObject.Opacity = 1;
}

Ed ecco il codice relativo alla Silverlight/Blend behavior:

Listato 6: Silverlight/Blend behavior

public class TouchZoom : Behavior<FrameworkElement>
{
  protected override void OnAttached()
  {
    base.OnAttached();
    Touch.FrameReported += AssociatedObjectTouchFrameReported;
  }
  protected override void OnDetaching()
  {
    base.OnDetaching();
    Touch.FrameReported -= AssociatedObjectTouchFrameReported;
  }
  private void AssociatedObjectTouchFrameReported(object sender, TouchFrameEventArgs e)
  {
    if (this.TouchZoomEnabled)
    {
      TouchPointCollection tpCollection = e.GetTouchPoints(this.AssociatedObject);
      tpCollection.ToList().
        ForEach(tp =>
                        {
                          switch (tp.Action)
                          {
                            case TouchAction.Up:
                              this.AssociatedObjectTouchUp(sender, e);
                              break;
                            case TouchAction.Move:
                              this.AssociatedObjectTouchMove(sender, e);
                              break;
                            case TouchAction.Down:
                              this.AssociatedObjectTouchDown(sender, e);
                              break;
                            }
                        });
    }
  }
}

L'utilizzo della stessa in XAML è quindi molto semplice. Il codice che proponiamo contiene un riferimento ad una ulteriore Behavior denominata TouchDrag che abilita anche il trascinamento dell'oggetto nello schermo con il semplice movimento delle dita. L'implementazione completa è disponibile nel codice allegato all'articolo.

Listato 7: Codice Xaml per il trascinamento dell'oggetto

<UserControl x:Class="SilverlightMultiTouch.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:Behaviors="clr-namespace:SilverlightMultiTouch.Behaviors">
    <Canvas Background="Black">
        <Canvas>
            <interactivity:Interaction.Behaviors>
                <Behaviors:TouchDrag TouchDragEnabled="True"/>
                <Behaviors:TouchZoom TouchZoomEnabled="True"/>
            </interactivity:Interaction.Behaviors>
            <Image Source="Images/image.png" Height="597" Width="448" Canvas.Left="404" Canvas.Top="96"/>
        </Canvas>
    </Canvas>
</UserControl>

Utilizzo di gesture complesse: manipulation e inertia

L'impiego di una (o più) Blend behavior per l'abilitazione di funzionalità multi-touch rende, a mio avviso, il codice molto elegante, modificabile via Blend e semplice da riutilizzare in progetti diversi. A questo scopo, a partire dal Surface Manipulation and Inertia Sample for Silverlight già menzionato, ho realizzato una Silverlight/Blend Behavior disponibile per il download all'indirizzo http://gallery.expression.microsoft.com/en-us/MultiTouch.

Tale codice permette l'abilitazione delle seguenti gesture ad un generico FrameworkElement:

  • Drag/Traslazione dell'elemento con uno o più touch point;
  • Scale/Zoom con due dita;
  • Rotation/Rotazione con due dita;
  • Effetto "inertia" per l'applicazione di effetti di accelerazione/decelerazione alle gesture precedentemente elencate.

L'utilizzo in XAML è riportato di seguito:

Listato 8: manipolazione complesse

<Canvas>
  <Image Source="Images/Desert.jpg" x:Name="image1">
    <interactivity:Interaction.Behaviors>
      <behaviors:MultiTouchManipulationBehavior InertiaEnabled="True"
                                                TouchRotateEnabled="True"
                                                TouchTranslateEnabled="True"
                                                TouchScaleEnabled="True" />
    </interactivity:Interaction.Behaviors>
  </Image>
  <p:CoreSmoothStreamingMediaElement x:Name="video" Background="{StaticResource GlossyBlack_BackgroundGradient}"
                                     AutoPlay="True" SmoothStreamingSource="http://video3.smoothhd.com.edgesuite.net/ondemand/Big%20Buck%20Bunny%20Adaptive.ism/Manifest">
    <interactivity:Interaction.Behaviors>
      <behaviors:MultiTouchManipulationBehavior InertiaEnabled="True"
                                                TouchRotateEnabled="True"
                                                TouchTranslateEnabled="True"
                                                TouchScaleEnabled="True"/>
    </interactivity:Interaction.Behaviors>
  </p:CoreSmoothStreamingMediaElement>
  <Image Source="Images/Jellyfish.jpg" x:Name="image2">
    <interactivity:Interaction.Behaviors>
      <behaviors:MultiTouchManipulationBehavior InertiaEnabled="False"
                                                TouchRotateEnabled="False"
                                                TouchTranslateEnabled="True"
                                                TouchScaleEnabled="True"/>
    </interactivity:Interaction.Behaviors>
  </Image>
</Canvas>

Di seguito (figura 3) è possibile visualizzare l'utilizzo della Behavior in Expressio Blend

Figura 3: Utilizzo della MultiTouchManipulationBehavior in Blend

Utilizzo della MultiTouchManipulationBehavior in Blend

Conclusioni

In questo articolo abbiamo analizzato l'implementazione multi-touch presente in Silverlight e qualche esempio di applicazione impiegando i tipi disponibili nel namespace System.Windows.Input e alcune implementazioni di gesture disponibili in rete.

Tali implementazioni consentono, già da subito, lo sviluppo di applicazioni complesse con interfacce multi-touch avanzate in grado di essere eseguite all'interno di un browser impiegando semplicemente il plug-in Silverlight e non richiedendo quindi l'installazione del .NET framework.

Bibliografia

Note sull'autore

Davide Zordan è Microsoft Silverlight MVP (Most Valuable Professional) e Silverlight Insider. Vive e lavora come insegnante e consulente relativamente a Silverlight / WPF in provincia di Vicenza. È rintracciabile on-line all'indirizzo http://davidezordan.net

Ti consigliamo anche