Condividi tramite


Nuove frontiere per l'interfaccia utente

Silverlight, Windows Phone 7 e controllo Thumb multitouch

Charles Petzold

Scarica il codice di esempio

Charles PetzoldPer molti programmatori Silverlight c'è una buona notizia: una delle due interfacce di programmazione supportata da Windows Phone 7 è Silverlight (l'altra è XNA). I programmatori Silverlight potranno impiegare la conoscenza e le competenze acquisite per scrivere nuove applicazioni per il telefono, oltre a essere in grado di sviluppare programmi Silverlight per il Web e il telefono che condividono codice.

Naturalmente la condivisione del codice (in particolare il codice dell'interfaccia utente) non è così semplice come può sembrare. La versione di Silverlight utilizzata nei telefoni è denominata Silverlight per Windows Phone e rappresenta un'implementazione degli elementi di base di Silverlight 3. In fase di scelta di un'applicazione con codice condiviso, è opportuno esaminare attentamente la documentazione: per ogni classe Silverlight, la documentazione online indica gli ambienti supportati. All'interno di ogni classe, gli elenchi di proprietà, i metodi e gli eventi utilizzano icone per indicare il supporto Windows Phone 7.

Un'applicazione Silverlight per il Web riceve l'input dell'utente tramite tastiera, mouse e probabilmente multitouch. In un programma Windows Phone 7 quest'ultima tecnologia costituisce il metodo di input principale. Non è presente un mouse e, sebbene il telefono possa disporre di una tastiera hardware, per i programmi Silverlight è possibile utilizzare una tastiera virtuale (SIP, Soft Input Panel) e solo tramite controllo TextBox.

Se i programmi Silverlight esistenti non ricevono mai l'input direttamente tramite mouse o tastiera, ma esclusivamente tramite controlli, non sarà necessario eseguire la conversione multitouch. Se inoltre i programmi contengono una logica mouse specifica, è possibile mantenerla in fase di trasferimento del programma nel telefono.

Nel telefono gli eventi Touch principali vengono convertiti in eventi mouse, consentendo senza problemi l'utilizzo della logica mouse esistente. Un evento Touch principale rappresenta l'attività completa di un dito che tocca per la prima volta lo schermo quando le altre dita non hanno stabilito alcun contatto.

Il passaggio dal mouse al multitouch è piuttosto delicato: Silverlight per il Web e Silverlight per Windows Phone supportano entrambi l'evento statico Touch.FrameReported, ma per il multitouch si tratta più che altro di un'interfaccia di basso livello. Mi sono occupato di questo evento nell'articolo "Finger Style: Exploring Multi-Touch Support in Silverlight" nel numero di marzo 2010 (msdn.microsoft.com/magazine/ee336026).

Silverlight per Windows Phone supporta un sottoinsieme di eventi Manipulation che ha avuto origine dal Surface SDK ed è da sempre integrato in Windows Presentation Foundation (WPF). Si tratta di un esempio che dimostra come il multitouch stia diventando la tecnologia più comunemente utilizzata nelle varie fasi. Il telefono supporta esclusivamente funzioni di conversione e scalabilità, non di rotazione, e non implementa l'inerzia, sebbene siano sufficienti poche informazioni per implementarla in modo autonomo. Questi eventi Manipulation non sono ancora supportati nella versione Web di Silverlight.

Per concludere, se si desidera condividere codice tra Silverlight per il Web e Silverlight per Windows Phone, è possibile ricorrere a eventi mouse o all'evento Touch.FrameReported.

Un'opzione alternativa: il controllo Thumb

Esiste tuttavia un'altra opzione: se si necessita solo del supporto per la conversione di eventi Manipulation ed è indifferente ricevere l'input tramite mouse o multitouch, è disponibile un controllo in grado di fornire questo tipo di supporto in una forma estremamente semplice: il controllo Thumb.

Si tratta di un controllo poco noto, nascosto nello spazio dei nomi System.Windows.Controls.Primitives e destinato essenzialmente ai modelli ScrollBar e Slider. È tuttavia possibile utilizzarlo per altre attività e di recente ho iniziato a considerare il controllo Thumb come un'implementazione di alto livello della funzionalità di conversione degli eventi Manipulation.

Il controllo Thumb non è un controllo multitouch a tutti gli effetti, in quanto supporta solo un evento Touch alla volta. Esplorando in dettaglio il controllo Thumb, sarà tuttavia possibile sperimentare il supporto dell'elaborazione touch con la condivisione del codice tra un'applicazione Silverlight e un'applicazione Windows Phone 7.

Il controllo Thumb definisce tre eventi:

  • DragStarted viene attivato nel momento in cui l'utente tocca per la prima volta il controllo con un dito o tramite mouse.
  • DragDelta indica il movimento del mouse o di un dito in rapporto allo schermo.
  • DragCompleted indica il sollevamento del mouse o del dito.

L'evento DragDelta è accompagnato da argomenti di evento con le proprietà HorizontalChange e VerticalChange che indicano il movimento del mouse o del dito rispetto all'ultimo evento. Questo evento viene in genere gestito aggiungendo modifiche incrementali alle proprietà X e Y di un evento TranslateTransform impostato sulla proprietà RenderTransform di un elemento trascinabile oppure sulle proprietà Canvas.Left e Canvas.Top associate.

Nello stato predefinito, il controllo Thumb è piuttosto lineare. Come per altri controlli, le proprietà HorizontalAlignment e VerticalAlignment sono impostate su Stretch e pertanto il controllo Thumb riempie normalmente l'area consentita. In altri casi, il controllo Thumb di Silverlight è di soli quattro pixel quadrati. In Silverlight per Windows Phone il controllo Thumb è di 48 pixel quadrati, ma a livello visivo è di soli 24 pixel quadrati con un bordo trasparente di 12 pixel su tutti e quattro i lati.

È opportuno impostare almeno un valore esplicito per Height (altezza) e Width (larghezza) nel controllo Thumb. Nella Figura 1 vengono confrontate le versioni Silverlight e Windows Phone 7, con il tema di colore chiaro su scuro predefinito del telefono. Per entrambi ho impostato i valori Height e Width su 72 e il valore Background (sfondo) su Blue, che nella versione Silverlight diviene una sfumatura variabile nel momento in cui viene premuto il controllo Thumb. La proprietà Foreground viene ignorata dal controllo.

image: The Silverlight and Windows Phone Thumb Controls

Figura 1 Controlli Thumb in Silverlight e Windows Phone

Spesso è opportuno non solo ridimensionare il controllo Thumb, ma anche applicare un elemento ControlTemplate in grado di ridefinire gli aspetti grafici del controllo. Questo elemento può essere estremamente semplice.

Condivisione di controlli

Un utente desidera un controllo semplice che consenta il trascinamento di bitmap attorno alla schermata. Un approccio molto semplice consiste nell'inserire un elemento Image e un controllo Thumb in una griglia con un'unica cella, con il controllo Thumb di dimensioni identiche all'elemento Image a cui si va pertanto a sovrapporre. Se l'elemento ControlTemplate per il controllo Thumb è un rettangolo trasparente, il controllo sarà invisibile ma attiverà comunque eventi di trascinamento.

Proviamo a creare un controllo di questo tipo che possa essere utilizzato in progetti Silverlight e Windows Phone 7. Si presuppone di aver installato Strumenti di sviluppo per Windows Phone 7 (xbox.http://xbox.create.msdn.com). Questi strumenti consentono di creare progetti Windows Phone 7 in Visual Studio.

Iniziare a creare un progetto Silverlight 4 regolare denominato DragImage. La soluzione DragImage risultante conterrà il progetto DragImage regolare (che corrisponde al programma Silverlight stesso) e un progetto DragImage.Web (che ospita il programma Silverlight in una pagina HTML o ASP.NET).

Aggiungere ora un nuovo progetto di tipo Applicazione Windows Phone alla soluzione e denominarlo DragImage.Phone. Per evitare che il nome venga visualizzato nell'elenco dei programmi del telefono o dell'emulatore, modificare il nome visualizzato nell'attributo Title del tag App nel file WMAppManifest.xml.

Fare clic con il pulsante destro del mouse sul progetto DragImage.Web o DragImage.Phone per visualizzare un menu di scelta rapida in cui scegliere Imposta come progetto di avvio ed eseguire il programma Silverlight regolare o il programma Windows Phone 7. Un elenco a discesa della barra degli strumenti in Visual Studio consente di distribuire il programma del telefono in un dispositivo telefonico o nell'emulatore. Visual Studio non svilupperà i progetti se l'elenco è impostato per Dispositivo Windows phone 7 ma non è associato alcun telefono.

Nel progetto DragImage (il progetto Silverlight regolare) aggiungere un nuovo elemento di tipo Controllo utente Silverlight e denominarlo DraggableImage. Come da prassi, Visual Studio creerà i file DraggableImage.xaml e DraggableImage.xaml.cs per il controllo.

Nella Figura 2 è illustrato DraggableImage.xml con la struttura ad albero visuale del controllo. La griglia esterna standard denominata LayoutRoot occuperà le dimensioni effettive del contenitore del controllo. La griglia interna verrà allineata all'angolo superiore sinistro, ma è presente un elemento TranslateTransform assegnato alla relativa proprietà RenderTransform per spostarla all'interno della griglia esterna. Questa griglia interna contiene un elemento Image con un controllo Thumb nella parte superiore e la proprietà Template impostata su una struttura ad albero visuale contenente solo un rettangolo trasparente.

Figura 2 DraggableImage.xaml

<UserControl x:Class="DragImage.DraggableImage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  Name="ctrl">
    
  <Grid x:Name="LayoutRoot">
    <Grid HorizontalAlignment="Left"
          VerticalAlignment="Top">
      <Image Name="image" Stretch="None"
             Source="{Binding ElementName=ctrl, Path=Source}" />
      <Thumb DragDelta="OnThumbDragDelta">
        <Thumb.Template>
          <ControlTemplate>
            <Rectangle Fill="Transparent" />
          </ControlTemplate>
        </Thumb.Template>
      </Thumb>
      <Grid.RenderTransform>
        <TranslateTransform x:Name="translate" />
      </Grid.RenderTransform>
    </Grid>
  </Grid>
</UserControl>

La proprietà Source dell'elemento Image è associata alla proprietà Source del controllo stesso. Questa proprietà è definita nel file DraggableImage.xaml.cs indicato nella Figura 3. Il file elabora inoltre l'evento DragDelta dal controllo Thumb modificando le proprietà X e Y dell'elemento TranslateTransform.

Figura 3 DraggableImage.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace DragImage {
  public partial class DraggableImage : UserControl {
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source",
      typeof(ImageSource),
      typeof(DraggableImage),
      new PropertyMetadata(null));

    public DraggableImage() {
      InitializeComponent();
    }

    public ImageSource Source {
      set { SetValue(SourceProperty, value); }
      get { return (ImageSource)GetValue(SourceProperty); }
    }

    void OnThumbDragDelta(object sender, DragDeltaEventArgs args) {
      translate.X += args.HorizontalChange;
      translate.Y += args.VerticalChange;
    }
  }
}

Per condividere il controllo con il progetto Windows Phone 7, fare clic con il pulsante destro del mouse sul progetto DragImage.Phone e scegliere Aggiungi | Elemento esistente per visualizzare la finestra di dialogo Aggiungi elemento esistente. Passare alla directory del progetto DragImage. Selezionare DraggableImage.xaml e DraggableImage.xaml.cs senza fare clic sul pulsante Aggiungi. Fare clic sulla piccola freccia a destra del pulsante Aggiungi e scegliere Aggiungi come collegamento. I file verranno visualizzati nel progetto DragImage.Phone con piccole frecce sulle icone indicanti che i file sono condivisi tra i due progetti.

È ora possibile apportare modifiche ai file DraggableImage ed entrambi i progetti utilizzeranno le versioni riviste.

Per testarli è necessaria una bitmap, che è possibile archiviare in una directory Images all'interno di ciascun progetto (non è necessario eseguire copie della bitmap, ma è possibile aggiungerla alla directory Images mediante un collegamento).

Dovrebbero essere disponibili due file MainPage.xaml, uno del progetto Silverlight regolare e l'altro del progetto Windows Phone 7. In MainPage.xaml per il progetto Silverlight aggiungere un binding spazio dei nomi denominato (in genere) "locale":

xmlns:local="clr-namespace:DragImage"

È ora possibile aggiungere DraggableImage alla pagina:

<Grid x:Name="LayoutRoot" Background="White">
  <local:DraggableImage 
    Source="Images/BuzzAldrinOnTheMoon.png" />
</Grid>

La classe MainPage del progetto Windows Phone 7 si trova in uno spazio dei nomi denominato DragImage.Phone, mentre la classe condivisa DraggableImage si trova nello spazio dei nomi DragImage. È dunque necessario un binding spazio dei nomi XML per lo spazio dei nomi DragImage, che è possibile denominare "condiviso":

xmlns:shared="clr-namespace:DragImage"

È ora possibile aggiungere DraggableImage all'area dei contenuti della pagina:

<Grid x:Name="ContentPanel" 
  Grid.Row="1" Margin="12,0,12,0">
  <shared:DraggableImage 
    Source="Images/BuzzAldrinOnTheMoon.png" />
</Grid>

Questo è forse il metodo più semplice per condividere un controllo tra due progetti Silverlight, uno per il Web e uno per Windows Phone 7, e poiché viene utilizzato il controllo Thumb, entrambi i programmi risponderanno a eventi mouse o Touch.

Il codice scaricabile per la soluzione DragImage include inoltre un progetto denominato DragImage.Wpf, ovvero un programma WPF che utilizza anch'esso questo controllo. In genere la condivisione di controlli tra Silverlight e WPF risulta tuttavia più complessa rispetto alla condivisione tra Silverlight e Windows Phone 7.

Colore e risoluzione

Oltre all'input tramite eventi mouse e Touch, nel tentativo di condividere codice tra Silverlight e Windows Phone 7 è necessario risolvere altri due problemi: colore e risoluzione video.

Nel desktop la visualizzazione di Silverlight prevede testo nero su sfondo bianco (un programma Silverlight può comunque utilizzare la classe SystemColors per visualizzare i colori Windows selezionati dall'utente). Per impostazione predefinita, la visualizzazione di Windows Phone 7 prevede testo bianco su sfondo nero, tranne nel caso in cui l'utente modifichi il colore del tema in nero su bianco. Windows Phone 7 fornisce chiavi di risorse pratiche e predefinite, tra cui PhoneForegroundBrush e PhoneBackgroundBrush, per fare in modo che un programma utilizzi lo schema di colori selezionato.

Qualsiasi codice o markup condiviso tra Silverlight e Windows Phone 7 che utilizza colori espliciti dovrà determinare in qualche modo la piattaforma su cui è in esecuzione per poter ottenere i colori corretti.

Il problema della risoluzione video è più insidioso. Tutte le coordinate Silverlight sono in unità di pixel e la regola vale anche per il telefono. Un monitor per desktop presenta mediamente una risoluzione di circa 100 dpi (un monitor di 21" con 1600 × 1200 pixel o 2000 pixel in diagonale avrà ad esempio una risoluzione di 105 dpi). Per impostazione predefinita, la risoluzione del monitor è di 96 dpi, sebbene l'utente possa modificare questo valore ai fini di una migliore leggibilità dello schermo.

Un dispositivo Windows Phone 7 dispone di uno schermo con 480 × 800 pixel e una diagonale di 933 pixel. Lo schermo misura tuttavia solo 3,5" in diagonale, con una conseguente risoluzione di circa 264 dpi corrispondente a circa 2,75 volte la risoluzione di un monitor per desktop.

Gli elementi condivisi di una specifica dimensione visualizzati correttamente sul desktop appariranno pertanto di dimensioni ridotte sul telefono. La distanza di visualizzazione di un telefono è tuttavia ridotta rispetto a quella dei monitor per desktop e pertanto non sarà necessario aumentare di 2,75 volte le dimensioni degli elementi per poterli visualizzare correttamente sul telefono.

Per gli eventi Touch qual è la dimensione ideale del controllo Thumb? Secondo un criterio che mi è capitato di leggere, i touch target dovrebbero essere di circa 9 mm (oppure 0,25") in altezza e lunghezza. In un monitor per desktop con una risoluzione di 96 ppi questo valore equivale a 34 pixel, mentre sul telefono corrisponderà a 93 pixel.

D'altro canto, il pulsante standard in un dispositivo Windows Phone 7 è di soli 72 pixel in altezza, un valore piuttosto adeguato. Probabilmente il migliore approccio consiste nel procedere per tentativi finché non si ottiene una dimensione semplice da utilizzare, ma non troppo ingombrante.

Adattamenti

In genere i programmi eseguono l'adattamento automatico per la compatibilità con diverse piattaforme utilizzando direttive di preprocessore per la compilazione condizionale. Un programma Silverlight definisce il simbolo di compilazione condizionale SILVERLIGHT e un programma Windows Phone 7 definisce entrambi i simboli SILVERLIGHT e PHONE (è possibile visualizzare i simboli selezionando la scheda Compilazione nella pagina Proprietà). Verrà pertanto generato un codice simile al seguente:

#if PHONE
  // Code for Windows Phone 7
#else
  // Code for regular Silverlight
#endif

In alternativa, è possibile ricorrere a un'alternativa in fase di esecuzione, accedendo all'oggetto Environment.OSVersion. Se la proprietà Platform è PlatformID.WinCE e la proprietà Version.Major è 7 o superiore, il codice è in esecuzione in un dispositivo Windows Phone 7 (o Windows Phone 8 oppure 9).

È in teoria possibile definire sezioni condizionali di file XAML utilizzando i tag AlternateContent e Choice definiti nello spazio dei nomi per la compatibilità dei markup, ma a quanto pare non sono supportati in Silverlight.

XAML può tuttavia contenere associazioni di dati che possono fare riferimento a vari oggetti, a seconda della piattaforma. XAML può inoltre disporre di riferimenti StaticResource che recuperano vari oggetti per diverse piattaforme. È questo l'approccio che ho utilizzato nel programma TextTransform.

Ho creato la soluzione TextTransform in modo analogo alla soluzione DragImage. La soluzione contiene tre progetti: TextTransform (programma Silverlight), TextTransform.Web (progetto Web per ospitare il programma Silverlight) e TextTransform.Phone (Windows Phone 7).

Nel progetto Silverlight ho quindi creato un controllo TextTransformer che deriva da UserControl. Il controllo viene condiviso tra il progetto Silverlight e il progetto Windows Phone 7. Il controllo TextTransformer contiene una stringa di testo hardcoded (il termine "TEXT") circondata da un bordo con quattro controlli Thumb agli angoli. Spostando un controllo Thumb, verrà applicata una trasformazione non affine a Border e TextBlock, che funzionerà correttamente solo se il quadrilatero formato dal bordo non presenta angoli concavi.

Il file TextTransformer.xaml non crea un nuovo modello per il controllo Thumb, ma definisce uno stile, come illustrato nella Figura 4.

Figura 4 Stile per il controllo Thumb di TextTransformer.xaml

<Style x:Key="thumbStyle" TargetType="Thumb">
  <Setter Property="HorizontalAlignment" 
          Value="Left" />
  <Setter Property="VerticalAlignment" 
          Value="Top" />
  <Setter Property="Width" 
          Value="{StaticResource ThumbSize}" />
  <Setter Property="Height" 
          Value="{StaticResource ThumbSize}" />
  <Setter Property="RenderTransform">
    <Setter.Value>
      <TranslateTransform 
        X="{StaticResource HalfThumbOffset}"
        Y="{StaticResource HalfThumbOffset}" />
    </Setter.Value>
  </Setter>
</Style>

Osserviamo i riferimenti a ThumbSize e HalfThumbOffset. Sebbene l'elemento TextBlock in cui è visualizzato il testo ottenga la proprietà Foreground corretta grazie all'eredità delle proprietà, il bordo deve essere colorato in modo esplicito con lo stesso colore dello sfondo:

<Border Name="border"
        BorderBrush="{StaticResource ForegroundBrush}"
        BorderThickness="1">

Dove vengono definite queste risorse? Vengono definite in App.xaml. Il progetto Silverlight regolare include una raccolta Resources nel file App.xaml contenente:

<Application.Resources>
  <SolidColorBrush x:Key="BackgroundBrush" Color="White" />
  <SolidColorBrush x:Key="ForegroundBrush" Color="Black" />
  <system:Double x:Key="ThumbSize">36</system:Double>
  <system:Double x:Key="HalfThumbOffset">-18</system:Double>
</Application.Resources>

Il file App.xaml del programma Windows Phone 7 fa riferimento alle risorse predefinite per i due pennelli e definisce valori ThumbSize e HalfThumbOffset superiori:

<Application.Resources>
  <SolidColorBrush x:Key="BackgroundBrush"
     Color="{StaticResource PhoneBackgroundColor}" />
  <SolidColorBrush x:Key="ForegroundBrush"
     Color="{StaticResource PhoneForegroundColor}" />
  <system:Double x:Key="ThumbSize">96</system:Double>
  <system:Double x:Key="HalfThumbOffset">-48</system:Double>
</Application.Resources>

Nella Figura 5 è illustrato il programma in esecuzione nel browser e nella Figura 6 il programma in esecuzione nell'emulatore di Windows Phone 7. L'emulatore viene visualizzato al 50% delle dimensioni effettive, per compensare la densità di pixel superiore sul telefono.

image: The TextTransform Program in the Browser

Figura 5 Programma TextTransform nel browser

image: The TextTransform Program on the Phone Emulator

Figura 6 Programma TextTransform nell'emulatore del telefono

Queste tecniche indicano che la condivisione di codice tra desktop e telefono è una realtà. Se si desidera approfondire l'argomento, il Surface Toolkit per Windows Touch include un controllo SurfaceThumb per sviluppatori WPF (analogo al controllo Thumb, aggiunge tuttavia il supporto per il multitouch reale ed eventi nel caso in cui venga utilizzato il controllo Thumb). Per ulteriori informazioni consultare la pagina beta del Surface Toolkit per Windows Touch all'indirizzo msdn.microsoft.com/library/ee957351.

Charles Petzold collabora da molto tempo con la rivista MSDN Magazine*. Il suo nuovo libro, "Programming Windows Phone 7", è disponibile per il download gratuito all'indirizzo bit.ly/cpebookpdf.*

Un ringraziamento ai seguenti esperti tecnici per la revisione dell'articolo: Doug Kramer e Robert Levy