Passa ai contenuti principali

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:

Commenti

Post popolari in questo blog

VB.NET : Aggregare stringhe con LINQ

Tip facile facile, ma a qualcuno potrebbe servire. Supponiamo di avere una lista di stringhe (magari come risultato di una query LINQ) e di voler ottenere una stringa con la concatenazione delle stesse: Dim list = CreateList() Dim concatStr = (From s In list _ Select s).Aggregate( Function (currentString, nextString) currentString + nextString) MessageBox.Show(concatStr) Il metodo CreateList non ci interessa, in questo momento, ma crea una lista di oggetti String. Protected Function CreateList() As IEnumerable( Of String ) Dim list As String () = {" stringa1 ", " stringa2 ", " stringa3 ", " stringa4 ", " stringa5 "} Return list.AsEnumerable() End Function Questo metodo potrebbe restituire una qualsiasi lista di oggetti di cui, nella select successiva recuperiamo solo stringhe. La stessa tecnica è utilizzabile per concatenare stringhe inserendovi un carattere separatore Dim list = CreateList() Dim

VB.NET: SplashScreen con effetto fade-in

In questo post vorrei proporvi un modo per realizzare una splash screen per le nostre applicazioni Windows Form che appare progressivamente con un effetto fade. Supponiamo di avere il nostro progetto VB.NET in una soluzione Visual Studio 2008 in cui abbiamo il sorgente della nostra applicazione Windows Form. Inseriamo una splash screen utilizzando il menù Progetto->Aggiungi Nuovo Elemento e selezionando il tipo di elemento “Schermata Iniziale” A questo punto Visual Studio creerà, automaticamente, la schermata iniziale che possiamo personalizzare graficamente come vogliamo. Per poter fare in modo che questa finestra appaia nel momento in cui avviamo l’applicazione, è necessario aprire le proprietà del progetto e impostare la maschera di avvio: In questo modo, all’avvio dell’applicazione, la schermata appare immediatamente e scompare un attimo prima della visualizzazione della finestra dell’applicazione. Possiamo far apparire la schermata iniziale con un ef

VB.NET: Convertire un file DOC in RTF e PDF con office interop

In questo post vorrei proporvi del codice per poter convertire un file .doc in un file .rtf oppure .pdf utilizzando le API di interoperabilità di Office. Creeremo una classe, DocConverter, che esporrà le due funzionalità sopra citate. Cominciamo con il prevedere un attributo privato della classe che rappresenterà l’applicazione Word che utilizzeremo per la conversione. Creeremo l’istanza dell’attributo privato all’interno del costruttore della classe: Public Sub New () If Not CreateWordApp() Then Throw New ApplicationException(" Assembly di interoperabilità con Office non trovato! ") End If End Sub Private _wordApp As Word.ApplicationClass Protected Function CreateWordApp() As Boolean Dim retval = True Try _wordApp = New Word.ApplicationClass() _wordApp.Visible = False Catch ex As System.Exception _wordApp = Nothing retval = False End Try Return retval End Function La conve