Compartilhar via


Visão geral de eventos roteados

Este tópico descreve o conceito de eventos roteados no Windows Presentation Foundation (WPF). O tópico define a terminologia de eventos roteados, descreve como eventos roteados são roteados por uma árvore de elementos, resume como manipular eventos roteados e apresenta como criar seus próprios eventos roteados personalizados.

Pré-requisitos

Este tópico pressupõe que você tenha conhecimento básico do CLR (Common Language Runtime) e da programação orientada a objetos, bem como o conceito de como as relações entre os elementos do WPF podem ser conceituadas como uma árvore. Para seguir os exemplos neste tópico, você também deve entender XAML (Extensible Application Markup Language) e saber como escrever aplicativos ou páginas WPF muito básicos. Para obter mais informações, consulte Passo a passo: meu primeiro aplicativo da área de trabalho WPF e XAML no WPF.

O que é um evento roteado?

Você pode pensar sobre eventos roteados de uma perspectiva funcional ou de implementação. Ambas as definições são apresentadas aqui, pois algumas pessoas acham uma delas mais útil.

Definição funcional: um evento roteado é um tipo de evento que pode invocar manipuladores em vários ouvintes em uma árvore de elementos, em vez de apenas no objeto que acionou o evento.

Definição de implementação: um evento roteado é um evento CLR que é apoiado por uma instância da classe e é processado pelo sistema de RoutedEvent eventos Windows Presentation Foundation (WPF).

Um aplicativo WPF típico contém muitos elementos. Criados em código ou declarados em XAML, esses elementos existem em uma relação de árvore de elementos entre si. A rota de evento pode deslocar-se em um de dois sentidos dependendo da definição do evento, mas geralmente a rota se desloca partindo do elemento de origem e, em seguida, "propaga-se" para cima pela árvore de elementos até atingir a raiz da árvore de elementos (geralmente uma página ou uma janela). Esse conceito de propagação pode parecer familiar para você caso você tenha trabalhado com o modelo de objeto DHTML anteriormente.

Considere a seguinte árvore de elementos simples:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Essa árvore de elementos produz algo parecido com o seguinte:

Botões Sim, Não e Cancelar

Nessa árvore de elementos simplificada, a origem de um Click evento é um dos Button elementos, e o que Button foi clicado é o primeiro elemento que tem a oportunidade de manipular o evento. Mas se nenhum manipulador anexado Button aos atos no evento, o evento será propagado para cima até o Button pai na árvore de elementos, que é o StackPanel. Potencialmente, o evento borbulha para Border, e depois para além da raiz da página da árvore de elementos (não mostrada).

Em outras palavras, a rota do evento para este Click evento é:

Botão -> StackPanel -> Borda -> ...

Cenários de nível superior para eventos roteados

Veja a seguir um breve resumo dos cenários que motivaram o conceito de evento roteado e por que um evento CLR típico não era adequado para esses cenários:

Composição e encapsulamento do controle: vários controles no WPF têm um modelo de conteúdo avançado. Por exemplo, você pode colocar uma imagem dentro de um Button, o que efetivamente estende a árvore visual do botão. No entanto, a imagem adicionada não deve interromper o comportamento de teste de clique que faz com que um botão responda a um Click de seus conteúdos, mesmo que o usuário clique em pixels que tecnicamente fazem parte da imagem.

Pontos de anexo do manipulador singular: no Windows Forms, você teria que anexar o mesmo manipulador várias vezes para processar eventos que poderiam ser gerados de vários elementos. Eventos roteados permitem que você anexe esse manipulador somente uma vez conforme mostrado no exemplo anterior, além de usarem a lógica de manipulador para determinar a origem do evento, se necessário. Por exemplo, este pode ser o manipulador para o XAML mostrado anteriormente:

private void CommonClickHandler(object sender, RoutedEventArgs e)
{
  FrameworkElement feSource = e.Source as FrameworkElement;
  switch (feSource.Name)
  {
    case "YesButton":
      // do something here ...
      break;
    case "NoButton":
      // do something ...
      break;
    case "CancelButton":
      // do something ...
      break;
  }
  e.Handled=true;
}
Private Sub CommonClickHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
  Dim feSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
  Select Case feSource.Name
    Case "YesButton"
      ' do something here ...
    Case "NoButton"
      ' do something ...
    Case "CancelButton"
      ' do something ...
  End Select
  e.Handled=True
End Sub

Manipulação de classe: eventos roteados permitem um manipulador estático que é definido pela classe. Esse manipulador de classe tem a oportunidade de manipular um evento antes que qualquer um dos manipuladores de instância anexados possa fazê-lo.

Fazendo referência a um evento sem reflexão: determinadas técnicas de código e marcação requerem uma maneira de identificar um evento específico. Um evento roteado cria um RoutedEvent campo como um identificador, que fornece uma técnica robusta de identificação de eventos que não requer reflexão estática ou em tempo de execução.

Como eventos roteados são implementados

Um evento roteado é um evento CLR que é apoiado por uma instância da classe e registrado no sistema de RoutedEvent eventos do WPF. A RoutedEvent instância obtida do registro normalmente é mantida como um public static readonly membro de campo da classe que se registra e, portanto, "possui" o evento roteado. A conexão com o evento CLR com nome idêntico (que às vezes é chamado de evento "wrapper") é realizada substituindo as add implementações e remove para o evento CLR. Normalmente, add e remove são deixados como um padrão implícito que usa a sintaxe de evento específico a um idioma apropriada para adicionar e remover manipuladores desse evento. O mecanismo de conexão e suporte de eventos roteados é conceitualmente semelhante a como uma propriedade de dependência é uma propriedade CLR apoiada pela DependencyProperty classe e registrada no sistema de propriedades do WPF.

O exemplo a seguir mostra a declaração de um evento roteado personalizado Tap , incluindo o registro e a exposição do RoutedEvent campo identificador e as add implementações e remove para o Tap evento CLR.

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
        add { AddHandler(TapEvent, value); }
        remove { RemoveHandler(TapEvent, value); }
}
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(MyButtonSimple))

' Provide CLR accessors for the event
Public Custom Event Tap As RoutedEventHandler
    AddHandler(ByVal value As RoutedEventHandler)
        Me.AddHandler(TapEvent, value)
    End AddHandler

    RemoveHandler(ByVal value As RoutedEventHandler)
        Me.RemoveHandler(TapEvent, value)
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Me.RaiseEvent(e)
    End RaiseEvent
End Event

Manipuladores de eventos roteados e XAML

Para adicionar um manipulador para um evento usando XAML, declare o nome do evento como um atributo no elemento que é um ouvinte de evento. O valor do atributo é o nome do seu método manipulador implementado, que deve existir na classe parcial do arquivo code-behind.

<Button Click="b1SetColor">button</Button>

A sintaxe XAML para adicionar manipuladores de eventos CLR padrão é a mesma para adicionar manipuladores de eventos roteados, porque você está realmente adicionando manipuladores ao wrapper de eventos CLR, que tem uma implementação de evento roteado por baixo. Para obter mais informações sobre como adicionar manipuladores de eventos em XAML, consulte XAML no WPF.

Estratégias de roteamento

Eventos roteados usam uma de três estratégias de roteamento:

  • Propagação: manipuladores de eventos na origem do evento são invocados. O roteamento do evento roteado ocorre então para sucessivos elementos pai até alcançar a raiz da árvore de elementos. A maioria dos eventos roteados usa a estratégia de roteamento por propagação. Eventos roteados por propagação geralmente são usados para relatar as alterações de entrada ou de estado de diferentes controles ou outros elementos de interface do usuário.

  • Direto: somente o próprio elemento de origem tem a oportunidade de invocar manipuladores em resposta. Isso é análogo ao "roteamento" que o Windows Forms usa para eventos. No entanto, ao contrário de um evento CLR padrão, os eventos roteados diretos dão suporte à manipulação de classe (a manipulação de classe é explicada em uma próxima seção) e podem ser usados por EventSetter e EventTrigger.

  • Túnel: inicialmente, os manipuladores de eventos na raiz da árvore de elementos são invocados. O evento roteado, em seguida, passa por sucessivos elementos filho ao longo de uma rota, em direção ao elemento de nó que é a origem do evento roteado (o elemento que acionou o evento roteado). Eventos roteados por túnel são frequentemente usados ou manipulados como parte da composição de um controle, de modo que eventos de partes compostas podem ser deliberadamente suprimidos ou substituídos por eventos que são específicos do controle completo. Os eventos de entrada fornecidos no WPF geralmente vêm implementados como um par de túnel/propagação. Eventos por túnel também são chamados de eventos de Visualização, devido a uma convenção de nomenclatura que é usada para os pares.

Por que usar eventos roteados?

Como desenvolvedor de aplicativos, você nem sempre precisa saber que o evento que você está tratando é implementado como um evento roteado, tampouco se preocupar com isso. Eventos roteados têm um comportamento especial, mas esse comportamento é praticamente invisível se você está manipulando um evento no elemento em que ele é acionado.

Os eventos roteados se tornam realmente poderosos quando você usa qualquer um dos cenários sugeridos: definição de manipuladores comuns em uma raiz comum, composição de seu próprio controle ou definição de sua própria classe de controle personalizado.

Ouvintes de eventos roteados e origens de eventos roteados não precisam compartilhar um evento em comum em sua hierarquia. Qualquer UIElement ou ContentElement pode ser um ouvinte de eventos para qualquer evento roteado. Portanto, você pode usar o conjunto completo de eventos roteados disponíveis em todo o conjunto de APIs de trabalho como uma "interface" conceitual por meio da qual elementos diferentes no aplicativo podem trocar informações de eventos. Esse conceito de "interface" para eventos roteados é especialmente aplicável para eventos de entrada.

Eventos roteados também podem ser usados para se comunicar por meio da árvore de elementos, porque os dados do evento para o evento são perpetuados para cada elemento na rota. Um elemento poderia modificar algo nos dados do evento e essa alteração estaria disponível para o próximo elemento da rota.

Além do aspecto de roteamento, há dois outros motivos pelos quais qualquer evento do WPF pode ser implementado como um evento roteado em vez de um evento CLR padrão. Se você estiver implementando seus próprios eventos, você também poderá considerar estes princípios:

  • Determinados recursos de estilo e modelagem do WPF, como EventSetter e EventTrigger exigem que o evento referenciado seja um evento roteado. Esse é o cenário de identificador do evento mencionado anteriormente.

  • Eventos roteados dão suporte a um mecanismo de manipulação de classe pelo qual a classe pode especificar métodos estáticos que têm a oportunidade de manipular eventos roteados antes que quaisquer manipuladores de instância registrados possam acessá-los. Isso é muito útil no design de controle, porque sua classe pode impor comportamentos de classe orientados a eventos que não podem ser acidentalmente suprimidos por meio da manipulação de um evento em uma instância.

Cada uma das considerações acima é discutida em uma seção separada deste tópico.

Adicionar e implementar um manipulador de eventos para um evento roteado

Para adicionar um manipulador de eventos em XAML, basta adicionar o nome do evento a um elemento como um atributo e definir o valor do atributo como o nome do manipulador de eventos que implementa um delegado apropriado, como no exemplo a seguir.

<Button Click="b1SetColor">button</Button>

b1SetColor é o nome do manipulador implementado que contém o código que manipula o Click evento. b1SetColor deve ter a mesma assinatura que o RoutedEventHandler delegado, que é o representante do manipulador de eventos para o Click evento. O primeiro parâmetro de todos os representantes de manipulador de eventos roteados especifica o elemento ao qual o manipulador de eventos é adicionado, enquanto o segundo parâmetro especifica os dados do evento.

void b1SetColor(object sender, RoutedEventArgs args)
{
  //logic to handle the Click event
}
Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
  'logic to handle the Click event
End Sub

RoutedEventHandler é o delegado básico do manipulador de eventos roteado. Para eventos roteados que são especializados para certos controles ou cenários, os delegados a usar para os manipuladores de eventos roteados também podem se tornar mais especializados, de modo que eles possam transmitir dados do evento especializados. Por exemplo, em um cenário de entrada comum, você pode manipular um DragEnter evento roteado. Seu manipulador deve implementar o DragEventHandler delegado. Usando o delegado mais específico, você pode processar o DragEventArgs no manipulador e ler a Data propriedade, que contém o conteúdo da área de transferência da operação de arrastar.

Para obter um exemplo completo de como adicionar um manipulador de eventos a um elemento usando XAML, consulte Manipular um evento roteado.

Adicionar um manipulador para um evento roteado em um aplicativo que é criado em código é simples. Os manipuladores de eventos roteados sempre podem ser adicionados por meio de um método AddHandler auxiliar (que é o mesmo método que o suporte existente chama .). add No entanto, os eventos roteados existentes do WPF geralmente têm implementações de suporte e add lógica que permitem que os manipuladores de eventos roteados sejam adicionados por uma sintaxe de evento específica da linguagem, que é uma sintaxe remove mais intuitiva do que o método auxiliar. A seguir está um exemplo de uso do método auxiliar:

void MakeButton()
 {
     Button b2 = new Button();
     b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
 }
 void Onb2Click(object sender, RoutedEventArgs e)
 {
     //logic to handle the Click event
 }
Private Sub MakeButton()
     Dim b2 As New Button()
     b2.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Onb2Click))
End Sub
 Private Sub Onb2Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
     'logic to handle the Click event     
 End Sub

O exemplo a seguir mostra a sintaxe do operador C# (o Visual Basic tem uma sintaxe de operador ligeiramente diferente devido ao tratamento da desreferenciação):

void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event
}
Private Sub MakeButton2()
  Dim b2 As New Button()
  AddHandler b2.Click, AddressOf Onb2Click2
End Sub
Private Sub Onb2Click2(ByVal sender As Object, ByVal e As RoutedEventArgs)
  'logic to handle the Click event     
End Sub

Para obter um exemplo de como adicionar um manipulador de eventos no código, consulte Adicionar um manipulador de eventos usando código.

Se você estiver usando o Visual Basic, também poderá usar a Handles palavra-chave para adicionar manipuladores como parte das declarações do manipulador. Para obter mais informações, consulte Manipulação de eventos WPF e do Visual Basic.

O conceito de manipulado

Todos os eventos roteados compartilham uma classe de banco de dados de eventos comum, RoutedEventArgs. RoutedEventArgs define a Handled propriedade, que assume um valor booleano. A finalidade da Handled propriedade é permitir que qualquer manipulador de eventos ao longo da rota marque o evento roteado como manipulado, definindo o valor de Handled to true. Depois de serem processados pelo manipulador em um elemento ao longo da rota, os dados do evento compartilhado são relatados novamente para cada ouvinte na rota.

O valor de Handled afeta como um evento roteado é relatado ou processado à medida que ele viaja mais ao longo da rota. Se Handled estiver true nos dados de evento de um evento roteado, os manipuladores que escutam esse evento roteado em outros elementos geralmente não são mais invocados para essa instância de evento específica. Isso é verdadeiro tanto para manipuladores anexados em XAML quanto para manipuladores adicionados por sintaxes de anexo de manipulador de eventos específicas da linguagem, como += ou Handles. Para os cenários de manipulador mais comuns, marcar um evento como manipulado definindo Handled como true "interromperá" o roteamento para uma rota de túnel ou uma rota de propagação e também para qualquer evento que seja tratado em um ponto da rota por um manipulador de classe.

No entanto, há um mecanismo "handledEventsToo" pelo qual os ouvintes ainda podem executar manipuladores em resposta a eventos roteados em que Handled estão true nos dados do evento. Em outras palavras, marcar os dados do evento como manipulados não interrompe realmente a rota de evento. Você só pode usar o mecanismo handledEventsToo no código ou em um EventSetter:

Além do comportamento que Handled o estado produz em eventos roteados, o conceito de tem implicações sobre como você deve projetar seu aplicativo e escrever o código do manipulador de Handled eventos. Você pode conceituar Handled como sendo um protocolo simples que é exposto por eventos roteados. Exatamente como você usa esse protocolo depende de você, mas o design conceitual de como o valor de Handled deve ser usado é o seguinte:

  • Se um evento roteado estiver marcado como manipulado, então ele não precisará ser manipulado novamente por outros elementos ao longo dessa rota.

  • Se um evento roteado não estiver marcado como manipulado, outros ouvintes que estavam anteriormente ao longo da rota optaram por não registrar um manipulador ou os manipuladores registrados optaram por não manipular os dados do evento e definir Handled como true. (Ou, é claro, é possível que o ouvinte atual seja o primeiro ponto na rota.) Os manipuladores no ouvinte atual agora têm três cursos de ação possíveis:

    • Não executar nenhuma ação: o evento permanece não manipulado e é roteado para o próximo ouvinte.

    • Executar código em resposta ao evento, mas determinar que a ação tomada não foi suficientemente substancial para justificar a marcação do evento como manipulado. O evento é roteado para o próximo ouvinte.

    • Executar código em resposta ao evento. Marque o evento como manipulado nos dados do evento passados para o manipulador de eventos, porque a ação foi considerada suficientemente substancial para garantir a marcação como manipulado. O evento ainda é roteado para o próximo ouvinte, mas com Handled=true seus dados de evento, portanto, apenas handledEventsToo os ouvintes têm a oportunidade de invocar outros manipuladores.

Esse design conceitual é reforçado pelo comportamento de roteamento mencionado anteriormente: é mais difícil (embora ainda seja possível em código ou estilos) anexar manipuladores para eventos roteados que são invocados mesmo que um manipulador anterior ao longo da rota já tenha definido Handled como true.

Para obter mais informações sobre Handleda manipulação de classe de eventos roteados e recomendações sobre quando é apropriado marcar um evento roteado como Handled, consulte Marcando eventos roteados como manipulados e manipulação de classe.

Em aplicativos, é bastante comum apenas tratar um evento roteado por propagação no objeto que o acionou e não se preocupar em absoluto com as características de roteamento do evento. No entanto, ainda é uma boa prática marcar o evento roteado como manipulado nos dados do evento para evitar efeitos colaterais não previstos caso um elemento que esteja mais acima na árvore de elementos também tenha um manipulador anexado para esse mesmo evento roteado.

Manipuladores de classe

Se você estiver definindo uma classe que deriva de alguma forma de DependencyObject, também poderá definir e anexar um manipulador de classe para um evento roteado que seja um membro de evento declarado ou herdado de sua classe. Manipuladores de classe são invocados antes de quaisquer manipuladores ouvintes de instância que são anexados a uma instância dessa classe, sempre que um evento roteado alcança uma instância de um elemento em sua rota.

Alguns controles WPF têm manipulação de classe inerente para determinados eventos roteados. Isso pode dar a impressão de que o evento roteado nunca é acionado, mas na verdade ele está sofrendo manipulação de classe e, se você usar determinadas técnicas, ainda será possível que o evento roteado seja manipulado por seus manipuladores de instância. Além disso, muitas classes base e controles expõem métodos virtuais que podem ser usados para substituir o comportamento de manipulação de classe. Para obter mais informações sobre como contornar manipulação de classe indesejada e sobre como definir sua própria manipulação de classe em uma classe personalizada, consulte Marcando eventos roteados como manipulados e manipulação de classe.

Eventos anexados no WPF

A linguagem XAML também define um tipo especial de evento chamado evento anexado. Um evento anexado permite que você adicione um manipulador de um determinado evento a um elemento arbitrário. O elemento manipulando o evento não precisa definir nem herdar o evento anexado; o objeto potencialmente lançando o evento e a instância de manipulação de destino também não precisam definir nem "ser proprietários" desse evento como um membro de classe.

O sistema de entrada do WPF usa eventos anexados extensivamente. No entanto, quase todos esses eventos anexados são encaminhados por meio de elementos base. Os eventos de entrada aparecem como eventos roteados não anexados equivalentes que são membros da classe do elemento base. Por exemplo, o evento Mouse.MouseDown anexado subjacente pode ser tratado mais facilmente em qualquer um determinado UIElement usando MouseDown on that UIElement em vez de lidar com a sintaxe do evento anexado em XAML ou código.

Para obter mais informações sobre eventos anexados no WPF, consulte Visão geral de eventos anexados.

Nomes de eventos qualificados em XAML

Outro uso de sintaxe que se assemelha à sintaxe de evento anexado nomedotipo.nomedoevento (mas não é, estritamente falando, um uso de evento anexado) é quando você anexa manipuladores para eventos roteados que são acionados por elementos filho. Você anexa os manipuladores a um pai comum para tirar proveito do roteamento de eventos, mesmo que o pai comum talvez não tenha o evento roteado relevante como membro. Considere este exemplo novamente:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Aqui, o ouvinte do elemento pai em que o manipulador é adicionado é um StackPanel. No entanto, ele está adicionando um manipulador para um evento roteado que foi declarado e será gerado pela classe (ButtonBasena verdade, mas disponível por meio de Button Button herança). Button "possui" o evento, mas o sistema de eventos roteados permite que manipuladores de qualquer evento roteado sejam anexados a qualquer UIElement ouvinte de ContentElement instância que poderia anexar ouvintes para um evento CLR (Common Language Runtime). O namespace xmlns padrão para esses nomes de atributo de evento qualificados normalmente é o namespace xmlns padrão do WPF, mas você também pode especificar namespaces prefixados para eventos roteados personalizados. Para obter mais informações sobre xmlns, consulte Namespaces XAML e mapeamento de namespace para XAML WPF.

Eventos de entrada WPF

Uma aplicação frequente de eventos roteados na plataforma WPF é para eventos de entrada. No WPF, os nomes de eventos roteados de túnel são prefixados com a palavra "Preview" por convenção. Eventos de entrada geralmente vêm em pares, com um deles sendo um evento por propagação e o outro sendo um evento por túnel. Por exemplo, o KeyDown evento e o PreviewKeyDown evento têm a mesma assinatura, sendo o primeiro o evento de entrada de propagação e o último o evento de entrada de túnel. Ocasionalmente, eventos de entrada só têm uma versão por propagação ou talvez somente uma versão roteada de modo direto. Na documentação, tópicos do evento roteado fazem referência cruzada a eventos roteados similares com estratégias alternativas de roteamento desde que tais eventos roteados existam, enquanto seções nas páginas de referência gerenciada esclarecem a estratégia de roteamento de cada evento roteado.

Os eventos de entrada do WPF que vêm em pares são implementados para que uma única ação do usuário da entrada, como pressionar um botão do mouse, gere os dois eventos roteados do par em sequência. Primeiro, o evento por túnel é acionado e desloca-se por sua rota. Então o evento por propagação é acionado e desloca-se por sua rota. Os dois eventos compartilham literalmente a mesma instância de dados de evento, pois a chamada de RaiseEvent método na classe de implementação que gera o evento de propagação escuta os dados de evento do evento de túnel e os reutiliza no novo evento gerado. Ouvintes com manipuladores para o evento por túnel têm a primeira oportunidade de marcar o evento roteado como manipulado (manipuladores de classe em primeiro lugar, em seguida, manipuladores de instância). Se um elemento na rota de túnel marcou o evento roteado como manipulado, os dados do evento já manipulado são enviados para o evento roteado por propagação e os manipuladores típicos anexados para os eventos de entrada por propagação equivalentes não são invocados. A aparência externa será como se o evento por propagação manipulado ainda não tivesse sido acionado. Esse comportamento de manipulação é útil para composição de controle, em que você pode desejar que todos os eventos de entrada baseados em teste de clique ou eventos de entrada baseados em foco, em vez de suas partes compostas, sejam relatados para seu controle final. O elemento de controle final é mais próximo da raiz na composição e, portanto, tem a oportunidade de realizar primeiro a manipulação de classe do evento por túnel e, talvez, de "substituir" esse evento roteado por um evento de controle mais específico, como parte do código que dá suporte à classe de controle.

Como uma ilustração de como um evento de entrada funciona, considere o exemplo de evento de entrada a seguir. Na ilustração da árvore a seguir, está a origem de um PreviewMouseDown evento e, em seguida, leaf element #2 de umMouseDown:

Diagrama de roteamento de eventos

A ordem de processamento de eventos é a seguinte:

  1. PreviewMouseDown (túnel) no elemento raiz.

  2. PreviewMouseDown (túnel) no elemento intermediário No. 1.

  3. PreviewMouseDown (túnel) no elemento de origem No. 2.

  4. MouseDown (propagação) no elemento de origem No. 2.

  5. MouseDown (propagação) no elemento intermediário No. 1.

  6. MouseDown (propagação) no elemento raiz.

Um delegado do manipulador de eventos roteados fornece referências para dois objetos: o objeto que acionou o evento e o objeto em que o manipulador foi invocado. O objeto em que o manipulador foi invocado é o objeto relatado pelo parâmetro sender. O objeto em que o evento foi gerado pela primeira vez é relatado pela Source propriedade nos dados do evento. Um evento roteado ainda pode ser gerado e manipulado pelo mesmo objeto, caso sender em que e Source são idênticos (esse é o caso das Etapas 3 e 4 na lista de exemplos de processamento de eventos).

Por causa do tunelamento e da propagação, os elementos pai recebem eventos de entrada em que o Source é um de seus elementos filho. Quando é importante saber qual é o elemento de origem, você pode identificar o elemento de origem acessando a Source propriedade.

Normalmente, uma vez que o evento de entrada é marcado Handled, outros manipuladores não são invocados. Normalmente, você deve marcar eventos de entrada como manipulados assim que um manipulador que atende sua manipulação lógica específica do aplicativo do significado do evento de entrada é invocado.

A exceção a essa instrução geral sobre Handled o estado é que os manipuladores de eventos de entrada registrados para ignorar Handled deliberadamente o estado dos dados do evento ainda seriam invocados ao longo de qualquer rota. Para obter mais informações, consulte Visualizar eventos ou Marcar eventos roteados como manipulados e manipulação de classe.

O modelo de dados do evento compartilhado entre eventos por túnel e eventos por propagação e o acionamento sequencial de eventos por túnel seguidos de eventos por propagação não é um conceito que seja geralmente verdadeiro para todos os eventos roteados. Esse comportamento é implementado especificamente pela forma como os dispositivos de entrada do WPF optam por gerar e conectar os pares de eventos de entrada. Implementar seus próprios eventos de entrada é um cenário avançado, mas você pode optar por seguir esse modelo também para seus próprios eventos de entrada.

Determinadas classes escolhem realizar a manipulação de classe de certos eventos de entrada, geralmente com a intenção de redefinir o significado de um determinado evento de entrada voltado para o usuário dentro desse controle e acionar um novo evento. Para obter mais informações, consulte Marcar eventos roteados como manipulados e manipulação de classe.

Para obter mais informações sobre entrada e como entrada e eventos interagem em cenários de aplicativos típicos, consulte Visão geral de entrada.

EventSetters e EventTriggers

Em estilos, você pode incluir alguma sintaxe de manipulação de eventos XAML pré-declarada na marcação usando um EventSetter. Quando o estilo é aplicado, o manipulador referenciado é adicionado à instância estilizada. Você pode declarar um EventSetter only para um evento roteado. A seguir, é mostrado um exemplo. Observe que o método b1SetColor referenciado aqui está em um arquivo code-behind.

<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.EventOvw2"
  Name="dpanel2"
  Initialized="PrimeHandledToo"
>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
    </Style>
  </StackPanel.Resources>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">
    Raise event, handle it, use handled=true handler to get it anyway.
  </Button>
</StackPanel>

A vantagem obtida aqui é que o estilo provavelmente conterá uma grande quantidade de outras informações que podem se aplicar a qualquer botão em seu aplicativo, e fazer parte desse estilo promove a EventSetter reutilização de código mesmo no nível de marcação. Além disso, um EventSetter método abstrai nomes para manipuladores um passo mais longe do aplicativo geral e da marcação de página.

Outra sintaxe especializada que combina o evento roteado e os recursos de animação do WPF é um EventTrigger. Assim como acontece com EventSettero , somente eventos roteados podem ser usados para um EventTrigger. Normalmente, an EventTrigger é declarado como parte de um estilo, mas an EventTrigger também pode ser declarado em elementos de nível de página como parte da Triggers coleção ou em um ControlTemplate. An EventTrigger permite que você especifique um Storyboard que é executado sempre que um evento roteado atinge um elemento em sua rota que declara um EventTrigger para esse evento. A vantagem de apenas EventTrigger manipular o evento e fazer com que ele inicie um storyboard existente é que an EventTrigger fornece melhor controle sobre o storyboard e seu comportamento em tempo de execução. Para obter mais informações, consulte Usar gatilhos de evento para controlar um storyboard depois de ele ser iniciado.

Mais informações sobre eventos roteados

Este tópico aborda principalmente eventos roteados da perspectiva de descrever os conceitos básicos e oferecer diretrizes sobre como e quando responder a eventos roteados que já estão presentes nos diversos controles e elementos base. No entanto, você pode criar seu próprio evento roteado em sua classe personalizada juntamente com todo o suporte necessário, assim como delegados e classes de dados do evento especializado. O proprietário do evento roteado pode ser qualquer classe, mas os eventos roteados devem ser gerados e manipulados por UIElement ou ContentElement classes derivadas para serem úteis. Para obter mais informações sobre eventos personalizados, consulte Criar um evento roteado personalizado.

Confira também