Visão geral da entrada
O subsistema WPF (Windows Presentation Foundation) fornece uma API poderosa para obter entrada de uma variedade de dispositivos, incluindo o mouse, o teclado, o toque e a caneta. Este tópico descreve os serviços fornecidos pelo WPF e explica a arquitetura dos sistemas de entrada.
API de entrada
A exposição da API de entrada primária é encontrada nas classes de elemento base: UIElement, ContentElement, FrameworkElemente FrameworkContentElement. Para obter mais informações sobre os elementos base, consulte Visão geral dos elementos base. Essas classes fornecem funcionalidade para eventos de entrada relacionados a pressionamentos de teclas, botões do mouse, roda do mouse, movimento do mouse, gerenciamento de foco e captura de mouse, para citar alguns. Ao colocar a API de entrada nos elementos base, em vez de tratar todos os eventos de entrada como um serviço, a arquitetura de entrada permite que os eventos de entrada sejam originados por um objeto específico na interface do usuário e dar suporte a um esquema de roteamento de eventos pelo qual mais de um elemento tem a oportunidade de lidar com um evento de entrada. Muitos eventos de entrada têm um par de eventos associados a eles. Por exemplo, o evento de pressionamento da tecla está associado aos eventos KeyDown e PreviewKeyDown. A diferença nesses eventos está em como eles são roteados para o elemento de destino. Eventos de visualização são roteados por túnel pela árvore de elementos, do elemento raiz ao elemento de destino. Eventos por propagação propagam-se para cima, do elemento de destino até o elemento raiz. O roteamento de eventos no WPF é discutido com mais detalhes posteriormente nesta visão geral e na visão geral de eventos roteados .
Classes de teclado e mouse
Além da API de entrada nas classes de elemento base, as classes Keyboard e Mouse fornecem APIs adicionais para trabalhar com entrada de teclado e mouse.
Exemplos de API de entrada na classe Keyboard são a propriedade Modifiers, que retorna a ModifierKeys pressionada no momento e o método IsKeyDown, que determina se uma tecla especificada é pressionada.
O exemplo a seguir usa o método GetKeyStates para determinar se um Key está no estado inferior.
// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison.
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
btnNone.Background = Brushes.Red;
}
' Uses the Keyboard.GetKeyStates to determine if a key is down.
' A bitwise AND operation is used in the comparison.
' e is an instance of KeyEventArgs.
If (Keyboard.GetKeyStates(Key.Return) And KeyStates.Down) > 0 Then
btnNone.Background = Brushes.Red
Exemplos de API de entrada na classe Mouse são MiddleButton, que obtém o estado do botão do meio do mouse, e DirectlyOver, que obtém o elemento sobre o qual o ponteiro do mouse está no momento.
O exemplo a seguir determina se o LeftButton no mouse está no estado Pressed.
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
UpdateSampleResults("Left Button Pressed");
}
If Mouse.LeftButton = MouseButtonState.Pressed Then
UpdateSampleResults("Left Button Pressed")
End If
As classes Mouse e Keyboard são abordadas com mais detalhes ao longo dessa visão geral.
Entrada de caneta
O WPF tem suporte integrado para o Stylus. O Stylus é uma entrada de caneta popularizada pelo Tablet PC. Os aplicativos WPF podem tratar a caneta como um mouse usando a API do mouse, mas o WPF também expõe uma abstração de dispositivo stylus que usa um modelo semelhante ao teclado e ao mouse. Todas as APIs relacionadas ao Stylus contêm a palavra "Stylus".
Como a caneta pode atuar como um mouse, os aplicativos que dão suporte apenas à entrada do mouse ainda podem obter algum nível de suporte de caneta automaticamente. Quando a caneta é usada de tal maneira, o aplicativo tem a oportunidade de lidar com o evento de caneta apropriado e, em seguida, manipula o evento de mouse correspondente. Além disso, serviços de nível superior, como entrada de tinta, também estão disponíveis por meio da abstração do dispositivo stylus. Para obter mais informações sobre a tinta como entrada, consulte Introdução à tinta.
Roteamento de eventos
Um FrameworkElement pode conter outros elementos como elementos filho em seu modelo de conteúdo, formando uma árvore de elementos. No WPF, o elemento pai pode participar de uma entrada direcionada a seus elementos filho ou outros descendentes por meio da manipulação de eventos. Isso é especialmente útil para criar controles a partir de controles menores, processo conhecido como "composição de controles" ou "composição". Para obter mais informações sobre árvores de elementos e como as árvores de elementos se relacionam com rotas de evento, consulte Árvores no WPF.
O roteamento de eventos é o processo de encaminhamento de eventos para vários elementos, de modo que um objeto ou elemento específico ao longo da rota possa optar por oferecer uma resposta significativa (por meio da manipulação) para um evento que pode ter sido originado por um elemento diferente. Eventos roteados usam um de três mecanismos de roteamento: direto, por propagação e por túnel. No roteamento direto, o elemento de origem é o único elemento notificado e o evento não é roteado para nenhum outro elemento. No entanto, o evento roteado direto ainda oferece alguns recursos adicionais que só estão presentes nos eventos roteados em comparação com os eventos CLR padrão. O roteamento por propagação ocorre de baixo para cima na árvore de elementos, notificando primeiro o elemento que originou o evento, depois o elemento pai e assim por diante. O processo de tunelamento começa na raiz da árvore de elementos e continua para baixo, terminando com o elemento original de origem. Para obter mais informações sobre eventos roteados, consulte Visão geral de eventos roteados.
Eventos de entrada do WPF geralmente vêm em pares que consistem em um evento por túnel e um evento por propagação. Eventos por túnel são diferenciados dos eventos por propagação com o prefixo "Preview". Por exemplo, PreviewMouseMove é a versão de túnel de um evento de movimento do mouse e MouseMove é a versão de propagação desse evento. Esse emparelhamento de eventos é uma convenção que é implementada no nível do elemento e não é uma funcionalidade inerente do sistema de eventos do WPF. Para obter detalhes, consulte a seção Eventos WPF de entrada em Visão geral de eventos roteados.
Manipulando eventos de entrada
Para receber a entrada em um elemento, um manipulador de eventos deve ser associado a esse evento específico. No XAML, isso é simples: você faz referência ao nome do evento como um atributo do elemento que estará escutando esse evento. Em seguida, defina o valor do atributo como o nome do manipulador de eventos que você define, com base em um delegado. O manipulador de eventos deve ser escrito em código como C# e pode ser incluído em um arquivo code-behind.
Eventos de teclado ocorrem quando o sistema operacional relata ações importantes que ocorrem enquanto o foco do teclado está em um elemento. Eventos de mouse e caneta se enquadram em duas categorias: eventos que relatam alterações na posição do ponteiro em relação ao elemento e eventos que relatam alterações no estado dos botões do dispositivo.
Exemplo de evento de entrada do teclado
O exemplo a seguir escuta um pressionamento de tecla de seta para a esquerda. Um StackPanel é criado e possui um Button. Um manipulador de eventos para escutar o pressionamento de tecla de seta para a esquerda está associado à instância de Button.
A primeira seção do exemplo cria o StackPanel e o Button e anexa o manipulador de eventos para o KeyDown.
<StackPanel>
<Button Background="AliceBlue"
KeyDown="OnButtonKeyDown"
Content="Button1"/>
</StackPanel>
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();
// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";
// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);
// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);
' Create the UI elements.
Dim keyboardStackPanel As New StackPanel()
Dim keyboardButton1 As New Button()
' Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue
keyboardButton1.Content = "Button 1"
' Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1)
' Attach event handler.
AddHandler keyboardButton1.KeyDown, AddressOf OnButtonKeyDown
A segunda seção é escrita em código e define o manipulador de eventos. Quando a tecla de seta para a esquerda é pressionada e o Button tem o foco do teclado, o manipulador é executado e a cor de Background do Button é alterada. Se a tecla for pressionada, mas não for a tecla de seta para a esquerda, a cor Background do Button será alterada de volta para a cor inicial.
private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
Button source = e.Source as Button;
if (source != null)
{
if (e.Key == Key.Left)
{
source.Background = Brushes.LemonChiffon;
}
else
{
source.Background = Brushes.AliceBlue;
}
}
}
Private Sub OnButtonKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
Dim source As Button = TryCast(e.Source, Button)
If source IsNot Nothing Then
If e.Key = Key.Left Then
source.Background = Brushes.LemonChiffon
Else
source.Background = Brushes.AliceBlue
End If
End If
End Sub
Exemplo de evento de entrada do mouse
No exemplo a seguir, a cor Background de um Button é alterada quando o ponteiro do mouse entra no Button. A cor Background é restaurada quando o mouse deixa o Button.
A primeira seção do exemplo cria o controle StackPanel e o controle Button e anexa os manipuladores de eventos para os eventos MouseEnter e MouseLeave ao Button.
<StackPanel>
<Button Background="AliceBlue"
MouseEnter="OnMouseExampleMouseEnter"
MouseLeave="OnMosueExampleMouseLeave">Button
</Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();
// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";
// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);
// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);
' Create the UI elements.
Dim mouseMoveStackPanel As New StackPanel()
Dim mouseMoveButton As New Button()
' Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue
mouseMoveButton.Content = "Button"
' Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton)
' Attach event handler.
AddHandler mouseMoveButton.MouseEnter, AddressOf OnMouseExampleMouseEnter
AddHandler mouseMoveButton.MouseLeave, AddressOf OnMosueExampleMouseLeave
A segunda seção do exemplo é escrita em código e define os manipuladores de eventos. Quando o mouse entra no Button, a cor Background do Button é alterada para SlateGray. Quando o mouse deixa a Button, a cor Background do Button é alterada de volta para AliceBlue.
private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
// Cast the source of the event to a Button.
Button source = e.Source as Button;
// If source is a Button.
if (source != null)
{
source.Background = Brushes.SlateGray;
}
}
Private Sub OnMouseExampleMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs)
' Cast the source of the event to a Button.
Dim source As Button = TryCast(e.Source, Button)
' If source is a Button.
If source IsNot Nothing Then
source.Background = Brushes.SlateGray
End If
End Sub
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
// Cast the source of the event to a Button.
Button source = e.Source as Button;
// If source is a Button.
if (source != null)
{
source.Background = Brushes.AliceBlue;
}
}
Private Sub OnMosueExampleMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs)
' Cast the source of the event to a Button.
Dim source As Button = TryCast(e.Source, Button)
' If source is a Button.
If source IsNot Nothing Then
source.Background = Brushes.AliceBlue
End If
End Sub
Entrada de texto
O evento TextInput permite que você ouça a entrada de texto de maneira independente do dispositivo. O teclado é o principal meio de entrada de texto, mas fala, manuscrito e outros dispositivos de entrada também podem gerar entrada de texto.
Para entrada de teclado, o WPF primeiro envia os eventos de KeyDown/KeyUp apropriados. Se esses eventos não forem tratados e a chave for textual (em vez de uma chave de controle, como setas direcionais ou teclas de função), um evento TextInput será gerado. Nem sempre há um mapeamento um-para-um simples entre eventos de KeyDown/KeyUp e TextInput porque vários pressionamentos de tecla podem gerar um único caractere de entrada de texto e pressionamentos de teclas individuais podem gerar cadeias de caracteres múltiplas. Isso é especialmente verdadeiro para idiomas como chinês, japonês e coreano que usam IMEs (Editores de Método de Entrada) para gerar os milhares de caracteres possíveis em seus alfabetos correspondentes.
Quando o WPF envia um evento KeyUp/KeyDown, Key é definido como Key.System caso os pressionamentos de teclas possam se tornar parte de um evento TextInput (se ALT+S for pressionado, por exemplo). Isso permite que o código em um manipulador de eventos KeyDown verifique se há Key.System e, se encontrado, deixe o processamento para o manipulador do evento TextInput gerado subsequentemente. Nesses casos, as várias propriedades do argumento TextCompositionEventArgs podem ser usadas para determinar os pressionamentos de tecla originais. Da mesma forma, se um IME estiver ativo, Key terá o valor de Key.ImeProcessede ImeProcessedKey fornecerá o(s) pressionamento(s) de tecla(s) original(is).
O exemplo a seguir define um manipulador para o evento Click e um manipulador para o evento KeyDown.
O primeiro segmento de código ou marcação cria a interface do usuário.
<StackPanel KeyDown="OnTextInputKeyDown">
<Button Click="OnTextInputButtonClick"
Content="Open" />
<TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";
// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);
// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);
' Create the UI elements.
Dim textInputStackPanel As New StackPanel()
Dim textInputeButton As New Button()
Dim textInputTextBox As New TextBox()
textInputeButton.Content = "Open"
' Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton)
textInputStackPanel.Children.Add(textInputTextBox)
' Attach event handlers.
AddHandler textInputStackPanel.KeyDown, AddressOf OnTextInputKeyDown
AddHandler textInputeButton.Click, AddressOf OnTextInputButtonClick
O segundo segmento de código contém os manipuladores de eventos.
private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
{
handle();
e.Handled = true;
}
}
private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
handle();
e.Handled = true;
}
public void handle()
{
MessageBox.Show("Pretend this opens a file");
}
Private Sub OnTextInputKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.Key = Key.O AndAlso Keyboard.Modifiers = ModifierKeys.Control Then
handle()
e.Handled = True
End If
End Sub
Private Sub OnTextInputButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
handle()
e.Handled = True
End Sub
Public Sub handle()
MessageBox.Show("Pretend this opens a file")
End Sub
Já que eventos de entrada são propagados para cima na rota de evento, o StackPanel recebe a entrada independentemente de qual elemento tem o foco do teclado. O controle TextBox é notificado primeiro e o manipulador de OnTextInputKeyDown
é chamado somente se o TextBox não tiver manipulado a entrada. Se o evento PreviewKeyDown for usado em vez do evento KeyDown, o manipulador de OnTextInputKeyDown
será chamado primeiro.
Neste exemplo, a lógica de tratamento é escrita duas vezes: uma vez para CTRL+O e novamente para o evento de clique do botão. Isso pode ser simplificado usando comandos, em vez de manipular os eventos de entrada diretamente. Os comandos são discutidos nesta visão geral e na Visão Geral de Comandos.
Toque e manipulação
O novo hardware e a API no sistema operacional Windows 7 fornecem aos aplicativos a capacidade de receber entrada de vários toques simultaneamente. O WPF permite que os aplicativos detectem e respondam ao toque de maneira semelhante à resposta a outras entradas, como o mouse ou o teclado, gerando eventos quando ocorre o toque.
O WPF expõe dois tipos de eventos quando ocorre o toque: eventos de toque e eventos de manipulação. Eventos de toque fornecem dados brutos sobre cada dedo em uma tela sensível ao toque e seu movimento. Eventos de manipulação interpretam a entrada como determinadas ações. Ambos os tipos de eventos são discutidos nesta seção.
Pré-requisitos
Você precisa dos seguintes componentes para desenvolver um aplicativo que responda ao toque.
Visual Studio 2010.
Windows 7.
Um dispositivo, como uma tela sensível ao toque, que dá suporte ao Windows Touch.
Terminologia
Os termos a seguir são usados quando o toque é discutido.
Toque é um tipo de entrada do usuário que é reconhecida pelo Windows 7. Normalmente, o toque é iniciado colocando os dedos em uma tela sensível ao toque. Observe que dispositivos como um touchpad, comuns em laptops, não suportam toque se o dispositivo simplesmente converter a posição e o movimento do dedo como entrada de mouse.
Multitouch é um toque que ocorre em mais de um ponto ao mesmo tempo. O Windows 7 e o WPF dão suporte a multitouch. Sempre que o toque é discutido na documentação do WPF, os conceitos se aplicam a multitouch.
Uma manipulação ocorre quando o toque é interpretado como uma ação física que é aplicada a um objeto. No WPF, os eventos de manipulação interpretam a entrada como uma tradução, expansão ou manipulação de rotação.
Um
touch device
representa um dispositivo que produz entrada por toque, como um único dedo em uma tela sensível ao toque.
Controles que respondem ao toque
Os controles a seguir podem ser rolados arrastando um dedo pelo controle caso ele tenha conteúdo que foi rolado para fora da área visível.
O ScrollViewer define a propriedade anexada ScrollViewer.PanningMode que permite especificar se o movimento panorâmico de toque está habilitado horizontalmente, verticalmente, ambos ou nenhum dos dois. A propriedade ScrollViewer.PanningDeceleration especifica a rapidez com que a rolagem diminui quando o usuário levanta o dedo da tela sensível ao toque. A propriedade anexada ScrollViewer.PanningRatio especifica a relação de deslocamento de rolagem para traduzir em deslocamento de manipulação.
Eventos de Toque
As classes base, UIElement, UIElement3De ContentElement, definem eventos aos quais você pode assinar para que seu aplicativo responda ao toque. Eventos de toque são úteis quando seu aplicativo interpreta o toque como algo diferente de manipular um objeto. Por exemplo, um aplicativo que permite que um usuário desenhe com um ou mais dedos assinaria eventos de toque.
Todas as três classes definem os eventos a seguir, que se comportam da mesma forma, independentemente da classe de definição.
Assim como os eventos de teclado e mouse, os eventos de toque são eventos roteados. Os eventos que começam com Preview
são eventos por túnel e os eventos que começam com Touch
são eventos por propagação. Para obter mais informações sobre eventos roteados, consulte Visão geral de eventos roteados. Ao lidar com esses eventos, você pode obter a posição da entrada, em relação a qualquer elemento, chamando o método GetTouchPoint ou GetIntermediateTouchPoints.
Para entender a interação entre os eventos de toque, considere o cenário em que um usuário coloca um dedo em um elemento, move o dedo no elemento e, em seguida, levanta o dedo do elemento. A ilustração a seguir mostra a execução dos eventos por propagação (os eventos por túnel são omitidos por questão de simplicidade).
Eventos de toque
A lista a seguir descreve a sequência dos eventos na ilustração anterior.
O evento TouchEnter ocorre uma vez quando o usuário coloca um dedo no elemento.
O evento TouchDown ocorre uma vez.
O evento TouchMove ocorre várias vezes à medida que o usuário move o dedo dentro do elemento.
O evento TouchUp ocorre uma vez quando o usuário levanta o dedo do elemento.
O evento TouchLeave ocorre uma vez.
Quando mais de dois dedos são usados, os eventos ocorrem para cada dedo.
Eventos de manipulação
Para casos em que um aplicativo permite que um usuário manipule um objeto, a classe UIElement define eventos de manipulação. Ao contrário dos eventos de toque que simplesmente relatam a posição do toque, os eventos de manipulação relatam como a entrada pode ser interpretada. Há três tipos de manipulações, tradução, expansão e rotação. A lista a seguir descreve como invocar os três tipos de manipulações.
Coloque um dedo em um objeto e mova o dedo pela tela sensível ao toque para invocar uma manipulação de tradução. Isso geralmente move o objeto.
Coloque dois dedos em um objeto e mova os dedos mais próximos ou mais distantes um do outro para invocar uma manipulação de expansão. Isso geralmente redimensiona o objeto.
Coloque dois dedos em um objeto e gire os dedos em volta um do outro para invocar uma manipulação de rotação. Isso geralmente gira o objeto.
Mais de um tipo de manipulação pode ocorrer simultaneamente.
Quando você faz com que os objetos respondam a manipulações, você pode fazer com que o objeto pareça ter inércia. Isso pode fazer com que seus objetos simulem o mundo físico. Por exemplo, quando você empurra um livro sobre uma mesa, se você empurrar com força suficiente, o livro continuará se movendo depois que você o soltar. O WPF permite que você simule esse comportamento gerando eventos de manipulação depois que os dedos do usuário liberam o objeto.
Para obter informações sobre como criar um aplicativo que permite que o usuário mova, redimensione e gire um objeto, consulte Passo a passo: Criando seu primeiro aplicativo touch.
O UIElement define os seguintes eventos de manipulação.
Por padrão, um UIElement não recebe esses eventos de manipulação. Para receber eventos de manipulação em um UIElement, defina UIElement.IsManipulationEnabled como true
.
O caminho de execução de eventos de manipulação
Considere um cenário em que um usuário "lança" um objeto. O usuário coloca um dedo no objeto, move o dedo pela tela sensível ao toque por uma curta distância e, em seguida, levanta o dedo enquanto ele está se movendo. O resultado disso é que o objeto se moverá sob o dedo do usuário e continuará se movendo depois que o usuário levantar o dedo.
A ilustração a seguir mostra o caminho de execução de eventos de manipulação e informações importantes sobre cada evento.
eventos de manipulação
A lista a seguir descreve a sequência dos eventos na ilustração anterior.
O evento ManipulationStarting ocorre quando o usuário coloca um dedo no objeto. Entre outras coisas, esse evento permite que você defina a propriedade ManipulationContainer. Nos eventos subsequentes, a posição da manipulação será relativa à ManipulationContainer. Em eventos diferentes de ManipulationStarting, essa propriedade é somente leitura, portanto, o evento ManipulationStarting é a única vez que você pode definir essa propriedade.
O evento ManipulationStarted ocorre em seguida. Esse evento relata a origem da manipulação.
O evento ManipulationDelta ocorre várias vezes à medida que os dedos de um usuário se movem em uma tela sensível ao toque. A propriedade DeltaManipulation da classe ManipulationDeltaEventArgs informa se a manipulação é interpretada como movimentação, expansão ou tradução. É aqui que você executa a maior parte do trabalho de manipulação de um objeto.
O evento ManipulationInertiaStarting ocorre quando os dedos do usuário perdem contato com o objeto. Esse evento permite que você especifique a desaceleração das manipulações durante a inércia. Isso é para que seu objeto possa emular diferentes espaços físicos ou atributos, se você escolher. Por exemplo, suponha que seu aplicativo tenha dois objetos que representam itens no mundo físico e um seja mais pesado que o outro. Você pode fazer com que o objeto mais pesado desacelere mais rápido do que o objeto mais leve.
O evento ManipulationDelta ocorre várias vezes à medida que ocorre inércia. Observe que esse evento ocorre quando os dedos do usuário se movem pela tela sensível ao toque e quando o WPF simula a inércia. Em outras palavras, ManipulationDelta ocorre antes e depois do evento ManipulationInertiaStarting. A propriedade ManipulationDeltaEventArgs.IsInertial informa se o evento ManipulationDelta ocorre durante a inércia, para que você possa verificar essa propriedade e executar ações diferentes, dependendo de seu valor.
O evento ManipulationCompleted ocorre quando a manipulação e qualquer inércia terminam. Ou seja, depois que todos os eventos ManipulationDelta ocorrerem, o evento ManipulationCompleted ocorrerá para sinalizar que a manipulação está concluída.
O UIElement também define o evento ManipulationBoundaryFeedback. Esse evento ocorre quando o método ReportBoundaryFeedback é chamado no evento ManipulationDelta. O evento ManipulationBoundaryFeedback permite que aplicativos ou componentes forneçam comentários visuais quando um objeto atinge um limite. Por exemplo, a classe Window manipula o evento ManipulationBoundaryFeedback para fazer com que a janela se mova ligeiramente quando sua borda é encontrada.
Você pode cancelar a manipulação chamando o método Cancel nos argumentos de evento em qualquer evento de manipulação, exceto ManipulationBoundaryFeedback evento. Quando você chama Cancel, os eventos de manipulação não são mais gerados e os eventos do mouse passam a corresponder ao toque. A tabela a seguir descreve a relação entre a hora em que a manipulação é cancelada e os eventos do mouse que ocorrem.
O evento no qual Cancel é chamado | Os eventos de mouse que ocorrem para entrada e que já ocorreram |
---|---|
ManipulationStarting e ManipulationStarted | Eventos de pressionamento do mouse. |
ManipulationDelta | Eventos de pressionamento do mouse e eventos de movimentação do mouse. |
ManipulationInertiaStarting e ManipulationCompleted | Pressionamento do mouse, movimentação do mouse e liberação do mouse. |
Observe que se você chamar Cancel quando a manipulação estiver em inércia, o método retornará false
e a entrada não gerará eventos do mouse.
A relação entre eventos de toque e manipulação
Um UIElement sempre pode receber eventos de toque. Quando a propriedade IsManipulationEnabled é definida como true
, um UIElement pode receber eventos de toque e manipulação. Se o evento TouchDown não for tratado (ou seja, a propriedade Handled é false
), a lógica de manipulação captura o toque no elemento e gera os eventos de manipulação. Se a propriedade Handled estiver definida como true
no evento TouchDown, a lógica de manipulação não gerará eventos de manipulação. A ilustração a seguir mostra a relação entre eventos de toque e eventos de manipulação.
Eventos de Toque e Manipulação
A lista a seguir descreve a relação entre os eventos de toque e manipulação mostrados na ilustração anterior.
Quando o primeiro dispositivo sensível ao toque gera um evento TouchDown em um UIElement, a lógica de manipulação chama o método CaptureTouch, que gera o evento GotTouchCapture.
Quando o GotTouchCapture ocorre, a lógica de manipulação chama o método Manipulation.AddManipulator, que gera o evento ManipulationStarting.
Quando os eventos TouchMove ocorrem, a lógica de manipulação gera os eventos de ManipulationDelta que ocorrem antes do evento ManipulationInertiaStarting.
Quando o último dispositivo de toque no elemento gera o evento TouchUp, a lógica de manipulação gera o evento ManipulationInertiaStarting.
Foco
Há dois conceitos principais que pertencem ao foco no WPF: foco no teclado e foco lógico.
Foco do teclado
O foco do teclado refere-se ao elemento que está recebendo entrada de teclado. Pode haver apenas um elemento em toda a área de trabalho que tenha o foco do teclado. No WPF, o elemento que tem foco no teclado terá IsKeyboardFocused definido como true
. O método estático KeyboardFocusedElement retorna o elemento que atualmente tem o foco do teclado.
O foco do teclado pode ser obtido navegando até um elemento usando a tecla Tab ou clicando em determinados elementos, como um TextBox. O foco do teclado também pode ser obtido programaticamente usando o método Focus na classe Keyboard. Focus tenta dar foco do teclado ao elemento especificado. O elemento retornado por Focus é o elemento que atualmente tem o foco do teclado.
Para que um elemento obtenha o foco do teclado, a propriedade Focusable e a propriedade IsVisible precisam ser definidas como true. Algumas classes, como Panel, têm Focusable definida como false
por padrão. Portanto, pode ser necessário definir essa propriedade como true
se você quiser que esse elemento seja capaz de obter o foco.
O exemplo a seguir usa Focus para definir o foco do teclado em um Button. O local recomendado para definir o foco inicial em um aplicativo está no manipulador de eventos Loaded.
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton);
}
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton)
End Sub
Para obter mais informações sobre o foco do teclado, consulte Visão geral do foco.
Foco Lógico
O foco lógico refere-se ao FocusManager.FocusedElement dentro de um escopo de foco. Pode haver vários elementos que têm foco lógico em um aplicativo, mas pode haver apenas um elemento que tenha foco lógico em um escopo de foco específico.
Um escopo de foco é um elemento contêiner que acompanha o FocusedElement dentro do escopo dele. Quando o foco sair de um escopo de foco, o elemento focado perderá o foco do teclado, mas manterá o foco lógico. Quando o foco retornar ao escopo de foco, o elemento focalizado obterá o foco do teclado. Isso permite que o foco do teclado seja alterado entre vários escopos de foco, mas garante que o elemento focado dentro do escopo de foco permaneça o elemento focado quando o foco retornar.
Um elemento pode ser transformado em um escopo de foco em XAML (Extensible Application Markup Language) definindo a propriedade anexada FocusManagerIsFocusScope para true
ou em código definindo a propriedade anexada usando o método SetIsFocusScope.
O exemplo a seguir transforma o StackPanel em um escopo de foco ao definir a propriedade IsFocusScope anexada.
<StackPanel Name="focusScope1"
FocusManager.IsFocusScope="True"
Height="200" Width="200">
<Button Name="button1" Height="50" Width="50"/>
<Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)
As classes no WPF que são escopos de foco por padrão são Window, Menu, ToolBare ContextMenu.
Um elemento que tem o foco do teclado também terá foco lógico para o escopo de foco ao qual pertence; portanto, definir o foco em um elemento usando o método Focus na classe Keyboard ou nas classes base do elemento tentará atribuir ao elemento foco de teclado e foco lógico.
Para determinar o elemento focado em um escopo de foco, use GetFocusedElement. Para alterar o elemento focado dentro de um escopo de foco, use SetFocusedElement.
Para obter mais informações sobre o foco lógico, consulte Visão geral do foco.
Posição do mouse
A API de entrada do WPF fornece informações úteis em relação aos espaços de coordenadas. Por exemplo, a coordenada (0,0)
é a coordenada superior esquerda, mas trata-se do canto superior esquerdo de qual elemento na árvore? O elemento que é o destino de entrada? O elemento ao qual você anexou seu manipulador de eventos? Ou outra coisa? Para evitar confusão, a API de entrada do WPF requer que você especifique seu quadro de referência ao trabalhar com coordenadas obtidas por meio do mouse. O método GetPosition retorna a coordenada do ponteiro do mouse em relação ao elemento especificado.
Captura do mouse
Os dispositivos do mouse contêm especificamente uma característica modal conhecida como captura de mouse. A captura do mouse é usada para manter um estado de entrada de transição quando uma operação do tipo "arrastar e soltar" é iniciada, de modo que outras operações que envolvem a posição nominal do ponteiro do mouse na tela não necessariamente ocorrem. Durante a ação de arrastar, o usuário não poderá clicar sem anular a ação do tipo "arrastar e soltar", o que torna a maioria das indicações mouseover inadequadas; enquanto isso, a captura do mouse é mantida pela origem da ação de arrastar. O sistema de entrada expõe APIs que podem determinar o estado de captura do mouse, bem como APIs que podem forçar a captura do mouse a um elemento específico ou limpar o estado de captura do mouse. Para obter mais informações sobre operações de arrastar e soltar, consulte Visão geral sobre arrastar e soltar.
Comandos
Os comandos permitem o tratamento de entrada em um nível mais semântico do que a entrada do dispositivo. Os comandos são diretivas simples, como Cut
, Copy
, Paste
ou Open
. Os comandos são úteis para centralizar sua lógica de comando. O mesmo comando pode ser acessado de um Menu, em um ToolBarou através de um atalho de teclado. Os comandos também fornecem um mecanismo para desabilitar controles quando o comando fica indisponível.
RoutedCommand é a implementação do WPF de ICommand. Quando um RoutedCommand é executado, um PreviewExecuted e um evento Executed são gerados no destino de comando, que fazem túnel e se propagam através da árvore de elementos como outras entradas. Se um destino de comando não estiver definido, o elemento com foco no teclado será o destino do comando. A lógica que executa o comando é anexada a um CommandBinding. Quando um evento Executed atinge um CommandBinding para esse comando específico, o ExecutedRoutedEventHandler no CommandBinding é chamado. Esse manipulador executa a ação do comando.
Para obter mais informações sobre comandos, consulte Visão geral do comando.
O WPF fornece uma biblioteca de comandos comuns que consiste em ApplicationCommands, MediaCommands, ComponentCommands, NavigationCommandse EditingCommandsou você pode definir o seu próprio.
O exemplo a seguir mostra como configurar um MenuItem para que, quando ele for clicado, ele invoque o comando Paste no TextBox, supondo que o TextBox tenha o foco do teclado.
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();
// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;
// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()
' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)
' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste
Para obter mais informações sobre comandos no WPF, consulte Visão geral do comando.
O sistema de entrada e os elementos base
Eventos de entrada, como os eventos anexados definidos pelas classes Mouse, Keyboarde Stylus, são gerados pelo sistema de entrada e injetados em uma posição específica no modelo de objeto com base no teste de acerto da árvore visual em tempo de execução.
Cada um dos eventos que Mouse, Keyboarde Stylus define como evento anexado é reexposto pelas classes de elemento base UIElement e ContentElement como um novo evento de roteamento. Os eventos roteados do elemento base são gerados por classes que manipulam o evento anexado original e reutilizam os dados do evento.
Quando o evento de entrada se torna associado a um elemento de origem específico por meio de sua implementação de evento de entrada de elemento base, ele pode ser roteado pelo restante de uma rota de evento que se baseia em uma combinação de objetos de árvore lógicos e visuais e ser manipulado pelo código do aplicativo. Geralmente, é mais conveniente lidar com esses eventos de entrada relacionados ao dispositivo usando os eventos roteados em UIElement e ContentElement, pois você pode utilizar uma sintaxe de manipulador de eventos mais intuitiva, tanto no XAML quanto no código. Você poderia optar por manipular o evento anexado que iniciou o processo, mas enfrentaria vários problemas: o evento anexado pode ser marcado como manipulado pelo tratamento da classe de elemento base e você precisa usar métodos acessadores em vez de sintaxe de evento verdadeiro para anexar manipuladores para eventos anexados.
O que vem a seguir
Agora você tem várias técnicas para lidar com a entrada no WPF. Você também deve ter uma compreensão aprimorada dos vários tipos de eventos de entrada e dos mecanismos de evento roteados usados pelo WPF.
Recursos adicionais estão disponíveis para explicar os elementos da estrutura do WPF e o roteamento de eventos com mais detalhes. Consulte as visões gerais a seguir para obter mais informações: Visão geral de comandos, Visão geral do foco, Visão geral de elementos base, Árvores no WPF e Visão geral de eventos roteados.
Consulte também
- Visão geral do foco
- Visão geral de comandos
- Visão geral de eventos roteados
- Visão geral dos elementos base
- Propriedades
.NET Desktop feedback