Este artigo foi traduzido por máquina.

Fronteiras da interface do usuário

Silverlight, Windows Phone 7 e o polegar multitoque

Charles Petzold

Baixe o código de exemplo

For many Silverlight programmers, the most exciting news about Windows Phone 7 is its support for Silverlight as one of its two programming interfaces. (A outra é o XNA.) Os programadores do Silverlight não só podem se basear em seu conhecimento e habilidades de criação de novos aplicativos para o telefone, mas devem ser capazes de desenvolver no Silverlight programas para a Web e o telefone que compartilhem código.

É claro que compartilhar código, principalmente de interface do usuário, raramente é tão fácil como parece à primeira vista. The version of Silverlight used in the phone is called Silverlight for Windows Phone, and it’s mostly a stripped-down implementation of Silverlight 3. Quando analisava um aplicativo de código compartilhado, você desejará dar uma olhada de perto a documentação: para cada classe do Silverlight, a documentação online indica quais ambientes são compatíveis com ela. Dentro de cada classe, listas de propriedades, métodos e eventos usam ícones para indicar suporte ao Windows Phone 7.

Um aplicativo do Silverlight para a Web obtém entrada do usuário pelo teclado, mouse e talvez por multitoque. Em um programa para Windows Phone 7, o multitoque é o principal meio de entrada. Não há mouse e, embora possa haver um hardware de teclado no telefone, os programas do Silverlight só podem se valer de um teclado virtual (o painel de entrada do software ou SIP) e apenas pelo controle TextBox.

Se os seus programas do Silverlight existentes nunca obtêm entrada direta pelo mouse ou teclado e dependem totalmente de controles, você não terá de se preocupar com a conversão para multitoque. Além disso, se os seus programas contêm uma lógica de mouse própria, na verdade você poderá mantê-la quando importar o programa para o telefone.

No telefone, os eventos de toque primários são convertidos em eventos de mouse, por isso a lógica de mouse existente deve funcionar bem. (Um evento de toque primário é toda a atividade de um dedo que toca na tela primeiro quando não há outros dedos em contato com ela.)

Mudar do mouse para multitoque exigirá um pouco de atenção: tanto o Silverlight para a Web quanto o Silverlight para Windows Phone dão suporte ao evento estático Touch.FrameReported, mas esse evento é uma interface de nível inferior para multitoque. Falei sobre esse evento no meu artigo “Estilo digital: Explorando o suporte a multitoque no Silverlight”, na edição de março de 2010 (msdn.microsoft.com/magazine/ee336026).

O Silverlight para Windows Phone dá suporte a um subconjunto dos eventos Manipulation que se originaram no SDK do Surface e, desde então, se tornaram parte do Windows Presentation Foundation (WPF). É um exemplo de como aos poucos o multitoque está se tornando cada vez mais uma tendência. O telefone é compatível apenas com as funções de colocação em escala e translação (não rotação) e não implementa a inércia, embora haja informações suficientes para que você mesmo a implemente. Esses eventos Manipulation ainda não têm suporte na versão do Silverlight para a Web.

Resumidamente, se você quiser compartilhar código entre o Silverlight para a Web e o Silverlight para Windows Phone, terá de manter os eventos de mouse ou Touch.FrameReported.

Considere o controle Thumb

No entanto, existe outra opção: se você só precisa do suporte a translação dos eventos Manipulation e não quer se preocupar se a entrada vem do mouse ou por toque, há um controle que oferece esse suporte de uma forma bastante pura. Este controle é o Thumb.

É possível que você nunca tenha se deparado com o Thumb. Esse controle fica oculto no namespace System.Windows.Controls.Primitives e é voltado principalmente para os modelos ScrollBar e Slider. Você também pode usá-lo para outros trabalhos, e recentemente comecei a considerar o Thumb como uma implementação de alto nível do recurso de translação dos eventos Manipulation.

Agora o controle Thumb não é verdadeiramente um controle “multitoque” — ele só permite um toque de cada vez. No entanto, se você explorar o Thumb um pouco terá a oportunidade de experimentar o suporte para computação por toque junto com o compartilhamento de código entre um aplicativo do Silverlight e um aplicativo do Windows Phone 7.

O controle Thumb define três eventos:

  • DragStarted é acionado quando o usuário toca o controle pela primeira vez com um dedo ou com o mouse.
  • DragDelta indica movimento do mouse ou do dedo na tela.
  • DragCompleted indica que o mouse ou o dedo foi tirado.

O evento DragDelta é acompanhado de argumentos de evento com as propriedades HorizontalChange e VerticalChange que indicam o movimento com o mouse ou o dedo desde o último evento. Normalmente você lida com esse evento adicionando alterações incrementais às propriedades X e Y de um TranslateTransform definido para uma propriedade RenderTransform de algum elemento que pode ser arrastado ou para as propriedades anexadas Canvas.Left e Canvas.Top.

Em seu estado padrão, Thumb é bastante simples. Assim como ocorre com outros controles, as propriedades HorizontalAlignment e VerticalAlignment são definidas como Stretch para que Thumb ocupe normalmente a área reservada a ele. Do contrário, o controle Thumb do Silverlight tem apenas quatro pixels quadrados. No Silverlight para Windows Phone, o Thumb tem 48 pixels quadrados, mas visualmente ele só tem mesmo 24 pixels quadrados com uma borda transparente de 12 pixels de largura em todos os quatro cantos.

No mínimo, provavelmente você queira definir Height e Width explícitos no controle Thumb. A Figura 1 mostra as versões para Silverlight e Windows Phone 7 lado a lado, com um tema de cores claras sobre escuras padrão do telefone. Para as duas, defini Height e Width como 72 e Background como Blue, que na versão para Silverlight torna-se um dégradé que muda quando Thumb é pressionado. Tampouco Thumb presta atenção na propriedade Foreground.

image: The Silverlight and Windows Phone Thumb Controls

Figura 1 Os controles Thumb para Silverlight e Windows Phone

Com muita frequência você desejará não somente redimensionar Thumb, mas também aplicar um ControlTemplate que redefina os elementos visuais do controle. Esse ControlTemplate pode ser extremamente simples.

Compartilhando controles

Vamos supor que você queira ter um controle simples que permita ao usuário arrastar bitmaps pela tela. Uma abordagem bastante simples é colocar um elemento Image e um Thumb em um Grid de uma única célula, com o controle Thumb tendo o mesmo tamanho que Image e o sobrepondo. Se o ControlTemplate de Thumb for um Rectangle transparente, o controle ficará invisível, mas ainda assim dispara os eventos de arrastar.

Vamos tentar criar um controle desse tipo que possa ser utilizado em projetos comuns do Silverlight e do Windows Phone 7. Vou pressupor que você tem as Ferramentas para Desenvolvedor do Windows Phone 7 instaladas (http://xbox.create.msdn.com). Essas ferramentas permitem criar projetos para Windows Phone 7 no Visual Studio.

Comece criando um projeto comum do Silverlight 4 chamado DragImage. A solução DragImage resultante contém o projeto DragImage usual (que é o programa do Silverlight propriamente dito) e um projeto DragImage.Web (que hospeda o programa do Silverlight em uma página HTML ou ASP.NET).

Em seguida, adicione à solução um novo projeto do tipo Aplicativo para Windows Phone. Nomeie esse projeto como DragImage.Phone. (É provável que você não queira que esse nome apareça na lista de programas do telefone ou no emulador de telefone, por isso pode alterar o nome para exibição no atributo Title da marca App no arquivo WMAppManifest.xml.)

Clique com o botão direito do mouse no projeto DragImage.Web ou DragImage.Phone para exibir um menu de contexto no qual é possível selecionar Definir como Projeto de Inicialização e executar o programa do Silverlight usual ou o programa do Windows Phone 7. Uma lista suspensa de barra de ferramentas no Visual Studio permite implantar o programa do telefone em um telefone ou no emulador de telefone. (O Visual Studio não criará os projetos se essa lista suspensa estiver definida para Dispositivo do Windows Phone 7 e se nenhum telefone estiver conectado.)

No projeto DragImage (o projeto comum para Silverlight), adicione um novo item do tipo Controle de Usuário do Silverlight. Nomeie-o como DraggableImage. Como de costume, o Visual Studio cria os arquivos DraggableImage.xaml e DraggableImage.xaml.cs para esse controle.

A Figura 2 mostra DraggableImage.xaml com a árvore visual do controle. O Grid externo padrão chamado LayoutRoot ocupará as dimensões completas do contêiner do controle; o Grid interno é alinhado no canto superior esquerdo, mas existe uma TranslateTransform atribuída à respectiva propriedade RenderTransform para movê-lo dentro do Grid externo. Esse Grid interno contém um elemento Image com um controle Thumb acima com sua propriedade Template definida como uma árvore visual contendo apenas um Rectangle transparente.

Figura 2 DraggableImage.xaml

<UserControl x:Class="DragImage.DraggableImage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://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>

Observe que a propriedade Source do elemento Image está ligada à propriedade Source do controle. Essa propriedade está definida no arquivo DraggableImage.xaml.cs mostrado na Figura 3. Esse arquivo também processa o evento DragDelta do controle Thumb alterando as propriedades X e Y de 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;
    }
  }
}

Para compartilhar esse controle com o projeto do Windows Phone 7, clique com o botão direito do mouse no projeto DragImage.Phone e selecione Adicionar | Item Existente para exibir a caixa de diálogo Adicionar Item Existente. Navegue até o diretório do projeto DragImage. Selecione DraggableImage.xaml e DraggableImage.xaml.cs, mas não clique no botão Adicionar. Em vez disso, clique na pequena seta à direita do botão Adicionar e selecione Adicionar como Vínculo. Os arquivos são exibidos no projeto DragImage.Phone com pequenas setas nos ícones, indicando que eles são compartilhados entre os dois projetos.

Agora você pode fazer alterações nos arquivos de DraggableImage, e os dois projetos usarão as versões revisadas.

Para testar, você precisará de um bitmap. Armazene o bitmap em um diretório Imagens dentro de cada um dos projetos. (Não é preciso fazer cópias do bitmap; você pode adicioná-lo ao diretório Imagens usando um link.)

Deve haver dois arquivos MainPage.xaml. Um é do projeto comum do Silverlight e o outro do projeto do Windows Phone 7. No arquivo MainPage.xaml do projeto do Silverlight, adicione uma ligação de namespace XML chamada (tradicionalmente) “local”:

xmlns:local="clr-namespace:DragImage"

Agora você pode adicionar DraggableImage à página:

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

A classe MainPage do projeto do Windows Phone 7 está em um namespace chamado DragImage.Phone, mas a classe DraggableImage compartilhada está no namespace DragImage. Você precisará de uma ligação de namespace XML para o namespace DragImage, que poderá chamar de “compartilhada”:

xmlns:shared="clr-namespace:DragImage"

Agora você pode adicionar DraggableImage à área de conteúdo da página:

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

That’s probably the simplest way you can share a control between two Silverlight projects, one for the Web and one for Windows Phone 7. Because the control uses the Thumb, both programs work with the mouse or touch.

O código para download da solução DragImage também inclui um projeto chamado DragImage.Wpf, que é um programa do WPF que também utiliza esse controle. No entanto, de um modo geral, compartilhar controles entre o Silverlight e o WPF é mais complicado do que fazê-lo entre o Silverlight e o Windows Phone 7.

Cor e resolução

À parte da entrada pelo mouse e por toque, quando tentar compartilhar código entre o Silverlight e o Windows Phone 7, você terá de lidar com duas outras questões: cor e resolução de vídeo.

Na área de trabalho, o Silverlight exibe texto em preto sobre um plano de fundo branco. (Contudo, um programa do Silverlight poderia usar a classe SystemColors para exibir as cores do Windows selecionadas pelo usuário.) Por padrão, o Windows Phone 7 exibe texto em branco em um plano de fundo preto, exceto se o usuário alterar o tema de cores para exibir preto sobre branco. O Windows Phone 7 fornece chaves de recursos práticas predefinidas, como PhoneForegroundBrush e PhoneBackgroundBrush, que ajudam um programa a usar o esquema de cores selecionado.

Qualquer código ou marcação entre o Silverlight e o Windows Phone 7 que usa cores explícitas terá de descobrir uma maneira de determinar a plataforma em que é executada para obter as cores corretas.

O problema da resolução de vídeo é um pouco mais trabalhoso. Todas as coordenadas do Silverlight estão em unidades de pixels, e essa regra também se aplica ao telefone. Um monitor de vídeo comum para desktop provavelmente tem resolução aproximada de 100 pontos por polegada (PPP). (Vamos supor, por exemplo, que um monitor de 21 polegadas processe 1600 × 1200 pixels ou 2000 pixels na diagonal. É uma resolução de 105 PPP.) Por padrão, o Windows pressupõe que a resolução de vídeo seja de 96 PPP, embora o usuário possa alterar esse valor para facilitar a leitura da tela.

Um dispositivo com o Windows Phone 7 tem uma tela de 480 × 800 pixels com 933 pixels na diagonal. Ainda assim a tela mede somente 3,5 polegadas na diagonal, o que significa que a resolução é de aproximadamente 264 PPP, cerca de 2,75 vezes a resolução do monitor do desktop.

Isso significa que os elementos compartilhados de um tamanho específico que têm uma boa exibição no desktop ficarão muito pequenos no telefone. Entretanto, a distância de visualização do telefone geralmente é menor do que para monitores de desktop, por isso os elementos não devem ser aumentados 2,75 vezes para ficarem visíveis no telefone.

Qual deve ser o tamanho de Thumb para fins de toque? Um critério que li indica que os destinos de toque devem ter 9 milímetros (ou 0,25 polegadas) de largura e de altura. Em um monitor de desktop com resolução de 96 pixels para a polegada, são 34 pixels — mas no telefone são 93 pixels.

Por outro lado, o botão padrão em um dispositivo com o Windows Phone 7 tem apenas 72 pixels de altura, e isso parece adequado. Talvez a melhor abordagem seja você testar até encontrar algo fácil de usar, mas que não seja muito desajeitado.

Fazendo ajustes

Tradicionalmente, os programas se ajustavam para as diferentes plataformas usando diretivas de pré-processador para a compilação condicional. Um programa do Silverlight define o símbolo de compilação condicional SILVERLIGHT, e um programa do Windows Phone 7 define SILVERLIGHT e PHONE. (Para ver esses símbolos, selecione a guia Compilar na página Propriedades do projeto.) Isso significa que seu código pode ser parecido com este:

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

Se preferir, você pode fazer a diferenciação em tempo de execução acessando o objeto Environment.OSVersion. Se a propriedade Platform é PlatformID.WinCE e a propriedade Version.Major é 7 ou um valor maior, o código está sendo executado em um dispositivo com o Windows Phone 7 (ou talvez Windows Phone 8 ou 9).

Teoricamente, é possível definir seções condicionais de arquivos XAML usando as marcas AlternateContent e Choice definidas no namespace de compatibilidade de marcação (mc), mas essas marcas parecem não ter suporte no Silverlight.

Porém, o XAML pode conter ligações de dados, e essas ligações podem fazer referência a diferentes objetos, de acordo com a plataforma. O XAML também pode ter referências a StaticResource que recuperam diferentes objetos para diferentes plataformas. É essa a abordagem que usei no programa TextTransform.

Criei a solução TextTransform da mesma forma que a solução DragImage. A solução tem três projetos: TextTransform (programa do Silverlight), TextTransform.Web (projeto Web para hospedar o programa do Silverlight) e TextTransform.Phone (Windows Phone 7).

No projeto do Silverlight, criei um controle TextTransformer que é derivado de UserControl. Esse controle é compartilhado entre o projeto do Silverlight e o projeto do Windows Phone 7. O controle TextTransformer contém uma cadeia de texto inserida no código (a palavra “TEXT”) cercada por um Border com quatro controles Thumb nos cantos. Mover um Thumb faz com que uma transformação não afim seja aplicada a Border e a TextBlock. (Isso só funciona bem se o quadrilátero formado por Border não tem cantos côncavos.)

O arquivo TextTransformer.xaml não cria um novo modelo para Thumb, mas define um Style como ilustrado na Figura 4.

Figura 4 O estilo Thumb de 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>

Observe as referências a ThumbSize e HalfThumbOffset. Embora o TextBlock que exibe o texto obtenha a propriedade Foreground correta por meio da herança de propriedade, Border deve ser colorido explicitamente com a mesma cor de primeiro plano:

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

Onde esses recursos são definidos? Em App.xaml. O projeto comum do Silverlight inclui uma coleção Resources em seu arquivo App.xaml, que contém o seguinte:

<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>

O arquivo App.xaml do programa do Windows Phone 7 faz referência aos recursos predefinidos dos dois pincéis e define valores maiores de ThumbSize e HalfThumbOffset:

<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>

A Figura 5 mostra o programa em execução no navegador e a Figura 6 mostra o programa em execução no emulador do Windows Phone 7. O emulador é exibido com 50% do tamanho total para compensar a densidade de pixels mais alta no telefone.

image: The TextTransform Program in the Browser

Figura 5 O programa TextTransform no navegador

image: The TextTransform Program on the Phone Emulator

Figura 6 O programa TextTransform no emulador de telefone

Essas técnicas sugerem que o compartilhamento de código entre o desktop e o telefone tornou-se uma realidade. Se você quiser se aprofundar mais um pouco neste assunto, o Kit de Ferramentas do Surface para Windows Touch inclui um controle SurfaceThumb para desenvolvedores de WPF. Ele é parecido com o controle Thumb normal, mas adiciona suporte para multitoque real e eventos para quando o polegar é movimentado com rapidez. Para obter mais informações, consulte a página da versão beta do Kit de Ferramentas do Surface para Windows Touch, em msdn.microsoft.com/library/ee957351.

Charles Petzold  is a longtime contributing editor to MSDN Magazine*.*His new book, “Programming Windows Phone 7,” is available as a free download at bit.ly/cpebookpdf.

Graças aos seguintes especialistas técnicos para revisão deste artigo: Doug Kramer and Robert Levy