Este artigo foi traduzido por máquina.

Estilo de dedo

Explorando o suporte a multitoque no Silverlight

Charles Petzold

Baixe o código de exemplo

Sempre que visito o Museu americano do histórico natural em Nova York, sempre faço um ponto para soltar em sobre o Hall da Primates. Uma grande seleção de esqueletos e espécimes de pelúcia, o corredor apresenta um panorama evolutiva da ordem Primate — animais no intervalo de tamanho de shrews árvore pequenas, lemurs e marmosets chimpanzees, excelente apes e seres humanos.

O que leaps deste anexo é uma característica surpreendentes comuns a todos os primates: a estrutura de bone de mão, incluindo um thumb opposable. A mesma organização das junções e dígitos permitiam nosso ancestrais e distantes primos Segure e subir ramificações da árvore permite que nossos espécies manipular o mundo que nos cerca e criar itens. Nossas mãos podem ter suas origens em patas de pequenos primates dezenas de milhões de anos atrás, mas também são um fator importante o que nos torna distintamente humano.

É nenhuma surpresa que podemos chegar instinctively para apontar ou até mesmo toquem objetos na tela do computador?

Em resposta a esse desejo humano para trazer os dedos para o mais profundo de conexão com o computador, dispositivos de entrada tenham sido evoluindo também. O mouse é excelente para selecionar e arrastar, mas hopeless para fazer um rascunho ou escrita de forma livre. A caneta eletrônica permite gravar, mas geralmente sentir estranha para esticar ou mover. Telas sensíveis ao toque são familiares dos caixas eletrônicos e Museu quiosques, mas geralmente são restritas a simples apontando e pressionando a tecla.

Acho que a tecnologia conhecida como “ multi-touch ” representa um grande avanço. Como o nome implica, multi-touch vai além das telas sensíveis ao toque do passado para detectar vários dedos e isso faz uma grande diferença nos tipos de movimentação e gestos que podem ser transmitidos por meio da tela. Multi-touch deixou de dispositivos de entrada orientados por toque do passado, mas ao mesmo tempo sugere uma paradigma de entrada intrinsecamente diferente.

Multi-touch provavelmente foi mais evidente em transmissões de notícias da televisão, com mapas de tela grande manipulados pelo meteorologist residente ou pundit. Microsoft tenha sido Explorando multi-touch de várias maneiras — do computador Microsoft Surface tamanho da tabela de café para dispositivos pequenos, como Zune HD — e a tecnologia está se tornando bastante padrão em smartphones também.

Enquanto o Microsoft superfície pode responder a muitas dedos simultâneos (e ainda contém câmeras internas para exibir objetos inseridos no vidro do), mais outros dispositivos multi-touch são limitados a um número distinto. Muitos respondem a apenas dois dedos — ou toque pontos, como são chamados. (I irá ser usando o dedo e ponto de contato bastante synonymously.) Mas sinergia esteja no trabalho aqui: Na tela do computador, dois dedos são mais de duas vezes tão eficientes como um.

A limitação de dois pontos de contato é característica dos monitores multi-touch foram disponibilizadas recentemente para computadores de mesa e laptops, bem como o laptop de 1420P Acer aspirar personalizado distribuídos para os participantes no Microsoft Professional Developers Conference (PDC) novembro passado — comumente conhecido como o laptop PDC. A distribuição de laptop PDC fornecido uma oportunidade única para milhares de desenvolvedores para escrever aplicativos com vários-touch reconhecimento.

Laptop PDC é a máquina que usei para explorar multi-touch suporte em Silverlight 3.

Eventos do Silverlight e classes

Suporte multi-touch está se tornando padrão nas várias APIs do Windows e estruturas. Suporte é incorporada ao Windows 7 e o próximo Windows Presentation Foundation (WPF) 4. (O computador Microsoft Surface é baseado em torno do WPF também, mas inclui as extensões personalizadas para seus recursos muito especiais.)

Neste artigo eu gostaria de enfocar o suporte multi-touch no Silverlight 3. O suporte é um pouco no lado da luz, mas certamente adequada e muito útil para explorar os conceitos básicos do multi-touch da.

Se você publicar um aplicativo do Silverlight multi-touch ao seu site da Web, que poderão utilizá-lo? O usuário precisará um multi-touch monitorar, é claro, mas também serão necessidade de estar executando o aplicativo do Silverlight em um sistema operacional e navegador que ofereça suporte multi-touch. Por enquanto, Internet Explorer 8 em execução no Windows 7 oferece esse suporte, e provavelmente mais navegadores e OSes aceitarão multi-touch no futuro.

O suporte ao Silverlight 3 multi-touch consiste em cinco classes, um delegado, uma enumeração e um único evento. Não é possível determinar se seu programa do Silverlight está sendo executado em um dispositivo multi-touch ou, se for, toque quantos aponta o dispositivo suporta.

Um aplicativo do Silverlight que deseja responder a multi-touch deve anexar um manipulador para o evento Touch.FrameReported estático:

Touch.FrameReported += OnTouchFrameReported;

Você pode anexar esse manipulador de eventos em máquinas que Don têm monitores multi-touch e defeituosos nada acontecerá. O evento FrameReported é o membro apenas público da classe estática toque. O manipulador tem esta aparência:

void OnTouchFrameReported(
  object sender, TouchFrameEventArgs args) {
  ...
}

Você pode instalar vários manipuladores de eventos Touch.FrameReported em seu aplicativo e todos eles reporta todos os eventos de contato em qualquer lugar no aplicativo.

TouchFrameEventArgs tem uma propriedade pública chamada TimeStamp ainda não tinha ocasião para usar e três métodos públicos essenciais:

  • TouchPoint GetPrimaryTouchPoint(UIElement relativeTo)
  • TouchPointCollection GetTouchPoints(UIElement relativeTo)
  • SuspendMousePromotionUntilTouchUp() void

O argumento GetPrimaryTouchPoint ou GetTouchPoints é usado unicamente para relatar informações sobre a posição do objeto TouchPoint.Você pode usar nulo para este argumento, informações de posicionamento será em relação ao canto superior esquerdo do aplicativo do Silverlight inteiro.

Multi-touch suporta vários dedos tocando a tela e cada dedo tocando a tela (backup para o número máximo, que atualmente é normalmente dois) é um ponto de contato.O ponto de contato primário refere-se a dedo que toca na tela quando nenhum outros dedos estão tocando a tela e o botão do mouse não está pressionado.

Toque em um dedo na tela.É o ponto de contato principal.Com o dedo primeiro ainda tocar na tela, colocar um segundo dedo na tela.Obviamente, essa segundo dedo não é um ponto de contato principal.Mas agora, com o dedo segundo ainda na tela, levante o dedo primeiro e colocá-la novamente na tela.É o principal ponto de contato?Não, não é.Um ponto de contato principal ocorre somente quando nenhum outros dedos estão tocando a tela.

Um ponto de contato principal mapas para o toque ponto que será promovido para o mouse.Em aplicativos reais multi-touch, tenha cuidado para não contar com o ponto de contato primário, porque o usuário será geralmente não anexar significado específico ao primeiro toque.

Eventos são disparados somente para dedos realmente tocar na tela.Não há nenhuma detecção de sobreposição de dedos bem perto da tela, mas não piscarão.

Por padrão, envolvendo os pontos de contato principal atividade é promovida a vários eventos de mouse.Isso permite que os seus aplicativos existentes responder ao toque, sem qualquer codificação especial.Tocando a tela se torna um evento MouseLeftButtonDown, movendo o dedo enquanto ele ainda está tocando a tela se torna um MouseMove e levantar o dedo é um MouseLeftButtonUp.

O objeto MouseEventArgs que acompanha as mensagens de mouse inclui uma propriedade chamada StylusDevice que ajuda a diferenciar os eventos de mouse de caneta e toque eventos.É minha experiência com o laptop PDC que a propriedade DeviceType é igual a TabletDeviceType.Mouse quando o evento do mouse e TabletDeviceType.Touch independentemente da se a tela é alterada com o dedo ou a caneta.

Somente o ponto de contato principal é promovido a eventos de mouse e, como o nome do terceiro método TouchFrameEventArgs sugere — pode inibir promoção.Obter mais informações sobre isso em breve.

Um Touch.FrameReported determinado evento pode ser acionado com base no ponto de contato de um ou vários pontos de contato.TouchPointCollection retornado do método GetTouchPoints contém todos os pontos de contato associados com um evento específico.TouchPoint retornado de GetPrimaryTouchPoint é sempre um ponto de contato principal.Se não houver nenhum ponto de contato principal associado ao evento específico, GetPrimaryTouchPoint retornará nulo.

Mesmo se TouchPoint retornado de GetPrimaryTouchPoint for nulo, não será o mesmo objeto como um dos objetos TouchPoint retornados do GetTouchPoints, embora todas as propriedades sejam os mesmos se o argumento passado para os métodos é o mesmo.

A classe TouchPoint define as seguintes quatro propriedades somente obtenção, tudo com a propriedades de dependência:

  • Ação do tipo TouchAction, uma enumeração com membros para baixo, mover e up.
  • Posição do tipo Point em relação ao elemento passado como um argumento para o método GetPrimaryTouchPoint ou GetTouchPoints (ou em relação ao canto superior esquerdo do aplicativo para um argumento nulo).
  • Tamanho do tipo de tamanho.Informações sobre o tamanho não estão disponível no laptop PDC para que eu não trabalhar com essa propriedade em todos os.
  • TouchDevice do tipo TouchDevice.

Você pode chamar o método SuspendMousePromotionUntilTouchUp do manipulador de eventos somente quando GetPrimaryTouchPoint retorna um objeto não-nulos e a propriedade Action é igual a TouchAction.Down.

O objeto TouchDevice tem duas propriedades somente obtenção também apoiadas por propriedades de dependência:

  • DirectlyOver do tipo UIElement — no elemento superior sob o dedo.
  • ID do tipo int.

DirectlyOver não precisa ser um filho do elemento passado para GetPrimaryTouchPoint ou GetTouchPoints.Esta propriedade pode ser nula se o dedo estiver no aplicativo do Silverlight (conforme definido pelas dimensões do objeto de plug-in do Silverlight), mas não dentro de uma área circundado por um controle de teste de hit.(Painéis com um pincel nulo do plano de fundo não são hit testável.)

A propriedade ID é crucial para distinguir entre vários dedos.Uma série específica de eventos associados com um dedo específico sempre iniciará com uma ação de desativação quando o dedo toca a tela, seguida pelos eventos mover, terminando com um evento de cima.Todos esses eventos serão associados a mesma identificação.(Mas Don suponha que um ponto de contato principal terá um valor de identificação 0 ou 1).

Mais código multi-touch não trivial irá fazer uso de um dicionário coleção onde a propriedade ID do TouchDevice é a chave de dicionário.Isso é como você armazenará as informações para um determinado dedo em eventos.

Examinando a eventos

Ao explorar um novo dispositivo de entrada, sempre é útil para escrever um pequeno aplicativo que registra os eventos na tela para que você possa obter uma idéia do que é como.Entre o código para download que acompanha este artigo é um projeto denominado MultiTouchEvents.Este projeto consiste em dois controles TextBox de lado a lado mostrando os eventos multi-touch para dois dedos.Se você tiver um monitor multi-touch pode executar este programa em charlespetzold.com/silverlight/MultiTouchEvents .

O arquivo XAML consiste em apenas um Grid de duas colunas que contém dois controles TextBox denominado txtbox1 e txtbox2.O arquivo de código é mostrado na 1 Figura .

Figura 1 de código para MultiTouchEvents

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace MultiTouchEvents {
  public partial class MainPage : UserControl {
    Dictionary<int, TextBox> touchDict = 
      new Dictionary<int, TextBox>();

    public MainPage() {
      InitializeComponent();
      Touch.FrameReported += OnTouchFrameReported;
    }

    void OnTouchFrameReported(
      object sender, TouchFrameEventArgs args) {

      TouchPoint primaryTouchPoint = 
        args.GetPrimaryTouchPoint(null);

      // Inhibit mouse promotion
      if (primaryTouchPoint != null && 
        primaryTouchPoint.Action == TouchAction.Down)
        args.SuspendMousePromotionUntilTouchUp();

      TouchPointCollection touchPoints = 
        args.GetTouchPoints(null);

      foreach (TouchPoint touchPoint in touchPoints) {
        TextBox txtbox = null;
        int id = touchPoint.TouchDevice.Id;
        // Limit touch points to 2
        if (touchDict.Count == 2 && 
          !touchDict.ContainsKey(id)) continue;

        switch (touchPoint.Action) {
          case TouchAction.Down:
            txtbox = touchDict.ContainsValue(txtbox1) ? 
              txtbox2 : txtbox1;
            touchDict.Add(id, txtbox);
            break;

          case TouchAction.Move:
            txtbox = touchDict[id];
            break;
 
          case TouchAction.Up:
            txtbox = touchDict[id];
            touchDict.Remove(id);
            break;
        }

        txtbox.Text += String.Format("{0} {1} {2}\r\n", 
          touchPoint.TouchDevice.Id, touchPoint.Action, 
          touchPoint.Position);
        txtbox.Select(txtbox.Text.Length, 0);
      }
    }
  }
}

Observe a definição do dicionário na parte superior da classe.O dicionário mantém o controle do que é associado com as identificações de ponto de duas toque TextBox.

Manipulador OnTouchFrameReported começa inibindo todos promoção de mouse.Esse é o único motivo para a chamada GetPrimaryTouchPoint e muito freqüentemente o único motivo você vai ser chamar esse método em um programa real.

Um loop foreach enumera através de membros TouchPoint do TouchPointCollection retornado de GetTouchPoints.Porque o programa contiver apenas dois controles TextBox e só é equipado para lidar com dois pontos de contato, ele ignora qualquer ponto de toque onde o dicionário já tem dois e a identificação não é esse dicionário.(Apenas como deseja que seus vários-touch Silverlight programa que reconhece para lidar com vários dedos, você Don quê-lo falha se ele encontrar muitos dedos!) A identificação é adicionada ao dicionário em um evento para baixo e removida do dicionário em um evento de cima.

Você perceberá que às vezes os controles TextBox obtém afetados para baixo com muito texto, e você precisará selecionar todo o texto e excluí-lo (CTRL - A, CTRL - X) para obter o programa funcionando sem problemas novamente.

O que você observará deste programa é que entrada multi-touch é capturada em um nível de aplicativo.Por exemplo, se você pressionar o dedo no aplicativo e, em seguida, movê-la para fora do aplicativo, o aplicativo continuará a receber eventos de mover e, eventualmente, um evento up quando você Levante o dedo.Na verdade, uma vez que um aplicativo está recebendo alguma multi-touch de entrada, entrada multi-touch para outros aplicativos é inibida e o cursor do mouse desaparece.

Esta captura centrado em aplicativos de entrada multi-touch permite que o aplicativo MultiTouchEvents muito certifique de si mesmo.Por exemplo, em mover e para baixo eventos o programa simplesmente supõe que a identificação será no dicionário.Em um aplicativo real, você pode querer prova mais marcador caso algo estranho ocorre, mas você terá sempre o evento para baixo.

Manipulação de dois finger

Um dos cenários multi-touch padrão é uma Galeria de fotos permite que você mover, redimensionar e Girar fotos com os dedos.Decidi tentar algo semelhante — apenas me dar um pouco familiaridade com os princípios envolvido — mais simples, mas também.Minha versão do programa tem somente um item único para manipular uma seqüência de caracteres de texto da palavra “ sensível ao toque. ”  Você pode executar o programa TwoFingerManipulation no meu site na Web em charlespetzold.com/silverlight/TwoFingerManipulation .

Quando você codificar um aplicativo para multi-touch, você irá provavelmente sempre Inibir mouse promoção para controles com vários-touch reconhecimento.Mas para tornar o seu programa utilizável sem um monitor multi-touch, você também irá adicionar processamento específico do mouse.

Se você tiver apenas um mouse ou um dedo único, você ainda poderá mover a seqüência de caracteres dentro do programa TwoFingerManipulation, mas você pode alterar apenas sua posição — a operação gráfica conhecida como conversão.Com dois dedos em uma tela multi-touch, você pode dimensionar o objeto e gire-a.

Quando eu-se para baixo com um teclado e da caneta para descobrir o algoritmo que seria preciso para esse dimensionamento e rotação, logo ficou evidente que não havia nenhuma solução exclusiva!

Suponha que um permanece dedo ptRef ponto fixo.(Todos os pontos aqui são em relação a uma superfície de exibição abaixo do objeto sendo manipulado). Outro dedo passa de ptOld ponto para ptNew.Como mostra a do Figura 2, você pode usar esses três pontos unicamente para calcular os fatores de escala horizontais e verticais do objeto.


Figura 2 movimentação duas finger convertida para fatores de escala

Por exemplo, a escala horizontal é o aumento na distância de ptOld.X e ptNew.X de ptRef.X, ou:

scaleX = (ptNew.X – ptRef.X) / (ptOld.X – ptRef.X)

Dimensionamento vertical é semelhante.Por exemplo do Figura 2, o fator de escala horizontal é 2 e o fator de escala vertical é ½.

Certamente, esta é a maneira mais fácil para codificar a ele.Ainda assim, o programa parece funcionar mais naturalmente se os dois dedos girar o objeto também.Isso é mostrado em do Figura 3.


Figura 3 movimentação duas finger convertida para rotação e escala

Primeiro, os ângulos de dois vetores — de ptRef para ptOld e de ptRef para ptNew — são calculados.(O método Math.Atan2 é ideal para esse trabalho.) Em seguida, ptOld é girado em relação ao ptRef pela diferença nesses ângulos.Este ptOld girado, em seguida, é usado com ptRef e ptNew para calcular os fatores de escala.Esses fatores de escala são muito menos porque um componente de rotação foi removido.

O algoritmo real (implementado no método ComputeMoveMatrix no arquivo translation from VPE for Csharp) tornou bastante fácil.No entanto, o programa necessário também um monte de código de suporte de transformação para compensar as deficiências as classes de transformação do Silverlight, não ter nenhum pública valor propriedade ou matriz multiplicação como do WPF.

No programa real, os dedos podem ser movendo ao mesmo tempo e manipular a interação entre os dois dedos é mais simples do que parece inicialmente.Cada movimento dedo é tratado independentemente usando outro dedo como ponto de referência.Apesar da crescente complexidade do cálculo, o resultado parece mais natural e acho que existe uma explicação simples: Na vida real, é muito comum para girar objetos com os dedos, mas é muito incomum para dimensioná-los.

Rotação é tão comum no mundo real que pode fazer sentido para implementá-lo quando um objeto é manipulado por apenas um dedo ou o mouse.Isso é demonstrado no programa AltFingerManipulation alternativo (executável em charlespetzold.com/silverlight/AltFingerManipulation ).Para dois dedos, o programa funciona da mesma como TwoFingerManipulation.Para um dedo, ele calcula uma rotação relativa ao centro do objeto e usa qualquer movimento excesso longe do centro para tradução.

Quebra automática de eventos com mais de eventos

Geralmente, eu gostaria de trabalhar com classes Microsoft ponderação fornece uma estrutura em vez de quebra-los no meu próprio código.Mas eu tinha em mente alguns aplicativos multi-touch que pensei se beneficiaria com uma interface de eventos mais sofisticada.

Primeiro, eu queria um sistema mais modular.Eu queria misturar controles personalizados que trataria sua própria entrada com o Silverlight existente por toque controles que permitem a entrada de toque simplesmente ser convertido em entrada do mouse.Eu também queria implementar captura.Embora o aplicativo do Silverlight captura o dispositivo multi-touch, eu queria controles individuais independentemente capturar um ponto de contato específico.

Eu também queria eventos Enter e deixar.De certa forma, esses eventos são o oposto de um paradigma de captura.Para entender a diferença, imagine que um teclado na tela piano onde cada chave é uma instância do controle PianoKey.Em primeiro lugar, você pode pensar dessas chaves como botões disparado mouse.Em um mouse para baixo de evento a chave de piano ativa uma anotação e um evento do mouse ela desativa a anotação.

Mas não o que você deseja para as chaves de piano.Você deseja a capacidade de executar o dedo do teclado para fazer com que os efeitos glissando para cima e para baixo.Se as chaves realmente não devem mesmo incomodar com desativação e de eventos.Eles são realmente apenas preocupada com eventos Enter e deixar.

WPF 4 e Microsoft Surface já já circulou eventos sensível ao toque e eles provavelmente estão chegando ao Silverlight no futuro.Mas eu atendido às minhas necessidades atuais com uma classe que chamei TouchManager, implementada no projeto de biblioteca de Petzold.MultiTouch na solução TouchDialDemos.Grande parte das TouchManager consiste em métodos estáticos, campos e um manipulador estático para o evento Touch.FrameReported que permite que ele gerencie eventos de toque em todo um aplicativo.

Uma classe que deseja registrar TouchManager cria uma instância da seguinte forma:

TouchManager touchManager = new TouchManager(element);

O argumento do construtor é de digite UIElement e geralmente é o elemento de criar o objeto:

TouchManager touchManager = new TouchManager(this);

Registrando com TouchManager, a classe indica que ela está interessada em todos os eventos multi-touch onde a propriedade DirectlyOver do TouchDevice é um filho do elemento transmitido ao construtor TouchManager e que esses eventos multi-touch não devem ser promovidos para eventos de mouse. Não é possível cancelar o registro de um elemento.

Depois de criar uma nova instância do TouchManager, uma classe pode instalar manipuladores para eventos nomeados TouchDown, TouchMove, Retoque, TouchEnter, TouchLeave e LostTouchCapture:

touchManager.TouchEnter += OnTouchEnter;

Todos os manipuladores são definidos de acordo com o delegado EventHandler < TouchEventArgs >:

void OnTouchEnter(
  object sender, TouchEventArgs args) {
  ...
}

TouchEventArgs define quatro propriedades:

  • Origem do tipo UIElement, que é o elemento originalmente transmitido ao construtor TouchManager.
  • Posição do tipo Point. Essa posição é relativo à origem.
  • DirectlyOver do tipo UIElement, simplesmente copiado do objeto TouchDevice.
  • Identificação do tipo int, também acabou de copiar do objeto TouchDevice.

Somente ao processar evento TouchDown é uma classe pode chamar o método de captura com a identificação de ponto de toque associada a esse evento:

touchManager.Capture(id);

Tudo ainda mais a entrada por toque para essa identificação vai para o elemento associado a esta instância TouchManager até que o evento retocar ou uma chamada explícita para ReleaseTouchCapture. Em ambos os casos, TouchManager aciona o evento LostTouchCapture.

Os eventos geralmente estão na ordem: TouchEnter, TouchDown, TouchMove, Retoque, TouchLeave e LostTouchCapture (se aplicável). É claro que pode haver vários eventos TouchMove entre TouchDown e retoque. Quando um ponto de contato não for capturado, pode haver vários eventos na ordem TouchLeave, TouchEnter e TouchMove como o ponto de contato deixa um elemento registrado e entra em outro.

O controle TouchDial

Alterações na entrada do usuário paradigmas geralmente exigem suposições antiga pergunta sobre o design adequado de controles e outros mecanismos de entrada. Por exemplo, alguns controles de GUI são tão solidly reconhecidos como a barra de rolagem e o controle deslizante. Você usar esses controles para navegar em documentos grandes ou imagens, mas também como pequeno volume controla em reprodutores de mídia.

Pois eu considerado fazendo na tela controle de volume responderia tocar, me perguntou se a antiga abordagem era realmente uma correta. No mundo real, controles deslizantes são às vezes, usados como controles de volume, mas geralmente restritos a painéis de mixagem profissionais ou equalizers gráficos. A maioria dos controles de volume no mundo real são disca. Uma discagem pode ser uma solução melhor para um controle de volume habilitado para toque?

Eu não suponhamos que eu tenho uma resposta definitiva, mas mostrarei como criar um.

O controle TouchDial é incluído na biblioteca Petzold.MultiTouch na solução TouchDialDemos (consulte o código de download para obter detalhes). TouchDial deriva RangeBase portanto ele pode aproveitar as propriedades Minimum, Maximum e Value — inclusive a lógica de coerção para manter o valor dentro do intervalo mínimo e máximo — e o evento ValueChanged. Mas em TouchDial, as propriedades Minimum, Maximum e Value são todos os ângulos em unidades de graus.

TouchDial responde para passar o mouse e toque e ele usa a classe TouchManager para capturar um ponto de contato. Com tanto as alterações TouchDial de entrada, mouse ou toque a propriedade Value durante um evento de mover com base no novo local e no local anterior do mouse ou finger relativa a um ponto central. A ação é bem semelhante ao do Figura 3, exceto pelo fato de que nenhuma escala estiver envolvido. O evento mover usa o método Math.Atan2 para converter coordenadas cartesianas em ângulos e, em seguida, adiciona a diferença em dois ângulos de implantação.

TouchDial não inclui um modelo padrão e, portanto, não tem nenhum aspecto visual padrão. Ao usar TouchDial, seu trabalho é fornecer um modelo, mas pode ser simples como alguns elementos. É claro que algo nesse modelo provavelmente deve girar de acordo com as alterações na propriedade Value. Para sua conveniência, TouchDial fornece uma propriedade de RotateTransform somente obtenção onde a propriedade Ângulo é igual a propriedade Value de RangeBase e as propriedades CenterX e CenterY refletem o centro do controle.

Figura 4 mostra um arquivo XAML com dois controles TouchDial que fazem referência a um estilo e um modelo definido como um recurso.

Figura 4 O arquivo XAML para o Project SimpleTouchDialTemplate

<UserControl x:Class="SimpleTouchDialTemplate.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:multitouch="clr-namespace:Petzold.MultiTouch;assembly=Petzold.MultiTouch">
  <UserControl.Resources>
    <Style x:Key="touchDialStyle" 
      TargetType="multitouch:TouchDial">
      <Setter Property="Maximum" Value="180" />
      <Setter Property="Minimum" Value="-180" />
      <Setter Property="Width" Value="200" />
      <Setter Property="Height" Value="200" />
      <Setter Property="HorizontalAlignment" Value="Center" />
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="multitouch:TouchDial">
            <Grid>
              <Ellipse Fill="{TemplateBinding Background}" />
              <Grid RenderTransform="{TemplateBinding RotateTransform}">
                <Rectangle Width="20" Margin="10"
                  Fill="{TemplateBinding Foreground}" />
              </Grid>
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </UserControl.Resources>
    
  <Grid x:Name="LayoutRoot">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
        
    <multitouch:TouchDial Grid.Column="0"
      Background="Blue" Foreground="Pink"
      Style="{StaticResource touchDialStyle}" />
        
    <multitouch:TouchDial Grid.Column="1"
      Background="Red" Foreground="Aqua"
      Style="{StaticResource touchDialStyle}" />
  </Grid>
</UserControl>

Observe que o estilo define a propriedade máximo 180 e os requisitos mínimos para -180 para permitir que a barra para ser giradas 180 graus para a esquerda e direita.(Estranhos, o programa não funcionava corretamente quando alternei a ordem dessas duas propriedades na definição de estilo.) A discagem consiste simplesmente em uma barra feita a partir de um elemento Rectangle dentro de uma elipse.A barra está dentro de um Grid de célula única que tem seu RenderTransform ligado à propriedade RotateTransform calculada pelo TouchDial.

O programa SimpleTouchDialTemplate é mostrado em execução em do Figura 5.


Figura 5 Programa do SimpleTouchDialTemplate

Você pode tentar check-out (juntamente com os programas de dois próximos que vai ser discutindo aqui) no charlespetzold.com/silverlight/TouchDialDemos.

Desativando a barra de dentro do círculo é um pouco estranho com o mouse e sentir muito mais natural com um dedo.Observe que você pode ativar a barra de quando você pressiona o botão esquerdo do mouse (ou colocar o dedo na tela) em qualquer lugar dentro do círculo.Enquanto liga a barra, você pode mover o mouse ou o dedo imediatamente porque ambos são capturados.

Se você quiser impedir que o usuário ativar a barra de, a menos que o mouse ou o dedo é pressionado diretamente sobre a barra, você pode definir a propriedade IsHitTestVisible de Ellipse como False.

Minha primeira versão do controle TouchDial não incluem a propriedade RotateTransform.Fazia sentido mais me que o modelo pode incluir um RotateTransform explícita em que a propriedade Ângulo foi o destino de um TemplateBinding para a propriedade Value do controle.No entanto, no Silverlight 3, as ligações Don funcionam em Propriedades de classes Don derivados de FrameworkElement, portanto, a propriedade ângulo de RotateTransform Don pode ser um destino de vinculação (isso está corrigido no Silverlight 4).

Rotação é sempre refere-se a um ponto central e esse fato pouco complica o controle TouchDial.TouchDial utiliza um ponto central de duas maneiras: para calcular os ângulos do Figura 3 mostrados e também para definir as propriedades CenterX e CenterY da RotateTransform cria.Por padrão, TouchDial calcula os dois centros como metade ActualWidth e ActualHeight propriedades, que é o centro do controle, mas há muito muitos casos em que não é muito o que você deseja.

Por exemplo, no modelo de do Figura 4, suponha que você deseja vincular a propriedade RenderTransform de Rectangle à propriedade RotateTransform do TouchDial.Ele não funcionará corretamente porque TouchDial é definir as propriedades CenterX e CenterY de RotateTransform a 100, mas o centro do retângulo em relação ao próprio é na verdade, o ponto (10, 90).Para permitir que você substituir esses padrões TouchDial calcula o tamanho do controle, o controle define propriedades RenderCenterX e RenderCenterY.Na propriedade SimpleTouchDialTemplate você pode definir essas propriedades no estilo da seguinte forma:

<Setter Property="RenderCenterX" Value="10" />
<Setter Property="RenderCenterY" Value="90" />

Ou, você pode definir essas propriedades como zero e definir RenderTransformOrigin do elemento Rectangle para indicar o centro em relação a mesmo:

RenderTransformOrigin="0.5 0.5"

Talvez também queira usar TouchDial em casos onde o ponto usado para referenciar o mouse ou movimento do dedo não está no centro do controle. Nesse caso, você pode definir as propriedades InputCenterX e InputCenterY para substituir os padrões.

Figura 6 mostra o arquivo XAML OffCenterTouchDial projeto.

Figura 6 O arquivo XAML OffCenterTouchDial

<UserControl x:Class="OffCenterTouchDial.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:multitouch="clr-namespace:Petzold.MultiTouch;assembly=Petzold.MultiTouch">
  <Grid x:Name="LayoutRoot">
    <multitouch:TouchDial Width="300" Height="200" 
      HorizontalAlignment="Center" VerticalAlignment="Center"
      Minimum="-20" Maximum="20"
      InputCenterX="35" InputCenterY="100"
      RenderCenterX="15" RenderCenterY="15">
      <multitouch:TouchDial.Template>
        <ControlTemplate TargetType="multitouch:TouchDial">
          <Grid Background="Pink">
            <Rectangle Height="30" Width="260"
              RadiusX="15" RadiusY="15" Fill="Lime"
              RenderTransform="{TemplateBinding RotateTransform}" />
            <Ellipse Width="10" Height="10"
              Fill="Black" HorizontalAlignment="Left"
              Margin="30" />
          </Grid>
        </ControlTemplate>
      </multitouch:TouchDial.Template>
    </multitouch:TouchDial>
  </Grid>
</UserControl>

Este arquivo contém um único controle TouchDial onde propriedades são definidas no próprio controle e a propriedade de modelo é definida como um modelo de controle contendo um Grid de célula única com um Rectangle e Ellipse. Ellipse é um ponto de pequenos dinâmica simbólico de Rectangle, você pode giratória para cima e para baixo por 20 graus, como mostra a do Figura 7.


Figura 7 Programa do OffCenterTouchDial

As propriedades InputCenterX e InputCenterY são sempre relativos ao todo o controle para que eles indicam o local do centro do elemento Ellipse em grade rosa. As propriedades RenderCenterX e RenderCenterY sempre são relativos à parte do controle ao qual a propriedade RotateTransform é aplicada.

Controles de volume e timbre pipes

Os dois exemplos anteriores demonstram como você pode dar uma aparência visual para TouchDial, ambos definindo a propriedade Template explicitamente na marcação ou, se precisar compartilhar modelos entre vários controles, fazendo referência a um ControlTemplate definidos como um recurso.

Você também pode derivar uma nova classe de TouchDial e usar o arquivo XAML exclusivamente para definir um modelo. Esse é o caso com RidgedTouchDial na biblioteca Petzold.MultiTouch. RidgedTouchDial é igual a TouchDial, exceto que ele tem um tamanho específico e a aparência visual (que você verá em breve).

Também é possível usar TouchDial (ou uma classe derivada como RidgedTouchDial) dentro de uma classe derivada de UserControl. A vantagem dessa abordagem é que você pode ocultar todas as propriedades definidas pelo RangeBase, incluindo Minimum, Maximum e Value e substituí-los por uma nova propriedade.

Esse é o caso com VolumeControl. VolumeControl deriva de RidgedTouchDial para sua aparência visual e define uma nova propriedade chamada volume. A propriedade volume respalda uma propriedade de dependência e alterações à propriedade emita um evento VolumeChanged.

O arquivo XAML para VolumeControl simplesmente faz referência ao controle RidgedTouchDial e define várias propriedades, incluindo Minimum, Maximum e Value:

<src:RidgedTouchDial 
  Name="touchDial"
  Background="{Binding Background}"
  Maximum="150"
  Minimum="-150"
  Value="-150"
  ValueChanged="OnTouchDialValueChanged" />

Assim, o TouchDial pode girar por 300 graus da posição mínima para a posição máxima. Figura 8 mostra o VolumeControl.xaml.cs. O controle converte o intervalo de 300 grau de discagem em escala logarítmica decibéis 0 a 96.

Figura 8 O ficheiro translation from VPE for Csharp para VolumeControl

using System;
using System.Windows;
using System.Windows.Controls;

namespace Petzold.MultiTouch {
  public partial class VolumeControl : UserControl {
    public static readonly DependencyProperty VolumeProperty =
      DependencyProperty.Register("Volume",
      typeof(double),
      typeof(VolumeControl),
      new PropertyMetadata(0.0, OnVolumeChanged));

    public event DependencyPropertyChangedEventHandler VolumeChanged;

    public VolumeControl() {
      DataContext = this;
      InitializeComponent();
    }

    public double Volume {
      set { SetValue(VolumeProperty, value); }
      get { return (double)GetValue(VolumeProperty); }
    }

    void OnTouchDialValueChanged(object sender, 
      RoutedPropertyChangedEventArgs<double> args) {

      Volume = 96 * (args.NewValue + 150) / 300;
    }

    static void OnVolumeChanged(DependencyObject obj, 
      DependencyPropertyChangedEventArgs args) {

      (obj as VolumeControl).OnVolumeChanged(args);
    }

    protected virtual void OnVolumeChanged(
      DependencyPropertyChangedEventArgs args) {

      touchDial.Value = 300 * Volume / 96 - 150;

      if (VolumeChanged != null)
        VolumeChanged(this, args);
    }
  }
}

Por que 96?Bem, embora a escala decibéis baseia-se em números decimais — sempre que aumenta a amplitude de um sinal por um fator multiplicative de 10, a intensidade aumenta linearmente em 20 decibéis — também é verdade que 10 para a 3ª potência é aproximadamente 2 à 10ª potência.Isso significa que quando a amplitude dobra, aumenta a intensidade em decibéis 6.Portanto, se você representar a amplitude com um valor de 16 bits — que é o caso com o som de CD e PC — obter um intervalo de 16 bits vezes 6 decibéis por bit ou 96 decibéis.

A classe PitchPipeControl também deriva de UserControl e define uma nova propriedade chamada freqüência.O arquivo XAML inclui um controle TouchDial, bem como uma série de TextBlocks para mostrar as anotações da oitava 12.Também faz uso de outra propriedade de TouchDial PitchPipeControl ainda não discuti ainda: Se você definir SnapIncrement como um valor diferente de zero em ângulos, o movimento de discagem não será suave, mas saltará entre incrementos.Como PitchPipeControl podem ser definidas para as notas da oitava 12, o SnapIncrement é definida como 30 graus.

Figura 9 mostra o programa PitchPipe que combina VolumeControl e PitchPipeControl.Você pode executar PitchPipe ao charlespetzold.com/silverlight/TouchDialDemos.


Figura 9 Programa do PitchPipe

O programa de bônus

Neste artigo eu mencionei que um controle denominado PianoKey no contexto de um exemplo.PianoKey é um controle real e é um dos vários controles no programa de piano, que você pode executar a charlespetzold.com/silverlight/Piano .O programa se destina a ser exibido com seu navegador maximizada.(Ou pressione F11 para tornar o Internet Explorer, vá para o modo de tela inteira e obter mais espaço.) Do Figura 10 mostra uma representação muito pequena.O teclado está dividido em sobreposição partes agudos e graves.Os pontos vermelhos indicam meio C.


Figura 10 programa do piano

É por este programa que escrevi TouchManager porque o programa de piano usa sensível ao toque de três maneiras diferentes.Discuti a VolumeControl azul, que captura o ponto de contato em um evento TouchDown e libera a captura de retoque.Os controles PianoKey que compõem os teclados também usam TouchManager, mas esses controles só ouvir os eventos TouchEnter e TouchLeave.Na verdade, você pode executar os dedos entre as chaves para efeitos glissando.Marrons retângulos que funcionam como sustentar pedais são controles do Silverlight ToggleButton comuns.Esses não são especificamente toque-ativados; em vez disso, pontos de contato são convertidas em eventos de mouse.

O programa de piano demonstra três maneiras diferentes de usar multi-touch.Suspeito que existem muitos, muitos outros.

 

Charles Petzold  é há muito tempo editor colaborador da MSDN Magazine.Seu livro mais recente é “ O Turing Annotated: A Guided Tour pelo papel histórico de Alan Turing sobre Computability e a máquina de Turing ” (Wiley, 2008).Blogs Petzold no seu site da charlespetzold.com .

Graças aos seguintes especialistas técnicos para revisar este artigo:  Robert Levy e Anson Tsao