lunedì 25 gennaio 2010

Briciole di WPF : InkCanvas e gesture

In questo post vorrei farvi vedere come poter riconoscere le gesture (cioè i movimenti) che l’utente può fare con il mouse, con le dita o con un eventuale stilo utilizzando il contenitore InkCanvas.

Il controllo InkCanvas, in realtà, si può utilizzare per intercettare i “gesti” dell’utente, per disegnare, per selezionare una porzione di interfaccia o per tutte queste funzionalità contemporaneamente.

Cominciamo col dire che per poter utilizzare l’InkCanvas solo per intercettare le gesture dell’utente è necessario impostare la proprietà EditingMode con il valore InkCanvasEditingMode.GestureOnly.

L’enumerazione InkCanvasEditingMode contiene tutti i possibili comportamenti dell’InkCanvas:

  • GestureOnly : il controllo rileva le gesture. In questo caso l’utente lascia un segno sulla superficie del controllo ma questo sparisce al termine della singola gesture;
  • EraseByPoint : permette di cancellare una parte di un precedente disegno passando il pennino come una gomma;
  • EraseByStroke : permette di cancellare un’intera curva precedentemente disegnata passando il pennino come una gomma;
  • Ink : l’utente può disegnare con il pennino;
  • InkAndGesture : l’utente può disegnare ma il controllo rileva anche le gesture;
  • None : il controllo non permette di disegnare e non rileva le gesture;
  • Select : il controllo permette di selezionare una porzione della superficie del controllo.

Concentriamoci, ora su come possiamo intercettare le gesture dell’utente.

Per poter far si che il controllo InkCanvas sia in grado di intercettare le gesture dobbiamo, inizialmente impostare la sopra citata proprietà, quindi istruire il controllo stesso su quali gesture siamo intenzionati ad intercettare.

Per fare questa ultima operazione dobbiamo utilizzare il metodo SetEnabledGestures che si aspetta come argomento un oggetto IEnumerable(of ApplicationGesture).

Ad esempio potremmo scrivere:

  1. Dim gestures = {ApplicationGesture.Circle,
  2.                 ApplicationGesture.Square,
  3.                 ApplicationGesture.Triangle}
  4. inkCanvas.SetEnabledGestures(gestures)

per istruire il controllo a rilevare i cerchi, i quadrati e i triangoli descritti dall’utente. Attenzione che, se non eseguiamo il metodo SetEnabledGestures

Istruito il controllo dobbiamo preoccuparci di gestire l’evento Gesture che è l’evento scatenato dal controllo ogni qual volta l’utente appoggia, muove e rilascia il pennino sulla superficie del controllo.
Dobbiamo subito precisare che tale evento viene scatenato sempre, per qualsiasi gesture dell’utente (anche se non è una di quelle per cui abbiamo istruito il controllo).

L’evento Gesture mette a disposizione, come argomento un oggetto di tipo InkCanvasGestureEventArgs, derivato da RoutedEventArgs, dal quale possiamo ricavare o l’insiame delle linee disegnate dall’utente (tramite la proprietà Strokes) oppure far riconoscere al framework quali sono state le gesture che l’utente ha tentato di eseguire.

Il primo caso è da utilizzare quando vogliamo interpretare, con un nostro algoritmo, le gesture dell’utente; il secondo caso, invece, delega al framework il riconoscimento rendendoci la vita più semplice.

Per recuperare l’elenco delle gesture interpretate dal framework possiamo utilizzare il metodo GetGestureRecognitionResults() della classe InkCanvasGestureEventArgs.
Questo metodo restituisce una collezione (sola lettura) di oggetti GestureRecognitionResult.

La classe GestureRecognitionResult contiene le informazioni relative alle gesture, ed in particolare possiamo utilizzare le due proprietà RecognitionConfidence e ApplicationGesture per capire esattamente quale gesture sono state eseguite dall’utente.

RecognitionConfidence è un enumerazione che può assumere i seguenti valori Poor, Intermediate e Strong ed indica la “precisione” con cui il framework ha interpretato la gesture.

ApplicationGesture, infine, contiene la gesture riconosciuta (se è una di quelle per cui il controllo è stato istruito) oppure NoGesture se la gesture non è riconosciuta.

Per testare il meccanismo di riconoscimento delle gesture ho preparato una semplice applicazione WPF che contiene un InkCanvas, un elenco di checkbox per selezionare le gesture da riconoscere, un livello minimo di riconoscimento delle gesture e un’area (costituita da un TextBlock) in cui verrà scritta la gesture eventualmente riconosciuta.

Il layout dell’applicazione è il seguente:

Layout_Applicazione_GestureIn questa window possiamo selezionare per quali gesture “istruire” l’InkCanvas e quale è il livello minimo desiderato di precisione della gesture.

In particolare, nel momento in cui selezioniamo (o deselezioniamo) una delle gesture mostrate nella lista di destra, viene richiamato il seguente codice:

  1. Private Sub AddApplicationGestures()
  2.     Me.inkCanvas.EditingMode = InkCanvasEditingMode.GestureOnly
  3.  
  4.     Dim gestures = (From c In Me.pnlGestureOptions.Children.OfType(Of Control)()
  5.                     Where TypeOf (c) Is CheckBox AndAlso CType(c, CheckBox).IsChecked
  6.                     Select CType(c.Tag, ApplicationGesture)).ToList()
  7.  
  8.     If gestures.Count = 0 Then
  9.         gestures.Add(ApplicationGesture.NoGesture)
  10.     End If
  11.     Me.inkCanvas.SetEnabledGestures(gestures)
  12.  
  13. End Sub

Il precedente metodo ricava i valori delle gestures dei CheckBox selezionati nel pannello di destra tramite una query LINQ e utilizza il metodo SetEnableGestures() per impostarli sul controllo InkCanvas. Se nessun CheckBox è selezionato (nessuna gesture) allora viene impostato il valore “NoGesture”.

Quando l’utente disegna una linea nell’InkCanvas, il controllo genera l’evento Gesture nel quale andiamo a verificare che l’utente abbia disegnato una delle gesture da noi selezionate:

  1. Private Sub inkCanvas_Gesture(ByVal sender As System.Object, ByVal e As System.Windows.Controls.InkCanvasGestureEventArgs)
  2.     Dim gestureResults As ReadOnlyCollection(Of GestureRecognitionResult)
  3.     gestureResults = e.GetGestureRecognitionResults()
  4.  
  5.     For Each gesture In gestureResults
  6.         If gesture.ApplicationGesture <> ApplicationGesture.NoGesture Then
  7.             If optPoor.IsChecked And gesture.RecognitionConfidence = RecognitionConfidence.Poor Then
  8.                 Me.txtResult.Background = New SolidColorBrush(Color.FromRgb(0, 255, 255))
  9.                 Me.txtResult.Text = gesture.ApplicationGesture.ToString()
  10.                 Exit For
  11.             End If
  12.             If optIntermediate.IsChecked And gesture.RecognitionConfidence = RecognitionConfidence.Intermediate Then
  13.                 Me.txtResult.Background = New SolidColorBrush(Color.FromRgb(255, 255, 255))
  14.                 Me.txtResult.Text = gesture.ApplicationGesture.ToString()
  15.                 Exit For
  16.             End If
  17.             If optStrong.IsChecked And gesture.RecognitionConfidence = RecognitionConfidence.Strong Then
  18.                 Me.txtResult.Background = New SolidColorBrush(Color.FromRgb(0, 255, 0))
  19.                 Me.txtResult.Text = gesture.ApplicationGesture.ToString()
  20.                 Exit For
  21.             End If
  22.         Else
  23.             Me.txtResult.Background = New SolidColorBrush(Color.FromRgb(255, 0, 0))
  24.             Me.txtResult.Text = gesture.ApplicationGesture.ToString()
  25.         End If
  26.     Next
  27. End Sub

Se la gesture è riconosciuta (ed ha, al minimo, il livello di precisione da noi selezionato) viene visualizzato il relativo valore con un opportuno colore (in base alla precisione), altrimenti viene visualizzato NoGesture.

L’applicazione mostrata è molto semplice ed inutile, ma può mostrare come interagire con l’InkCanvas per eseguire operazioni quando l’utente esegue particolari gesture.

Per scaricare i sorgenti della soluzione di prova (Visual Studio 2010) cliccare sull’icona seguente:

Nessun commento: