Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Embora não haja uma regra absoluta para quando marcar um evento roteado como tratado, considere marcar um evento como tratado se o código responder ao evento de forma significativa. Um evento roteado marcado como manipulado continuará ao longo de sua rota, mas somente manipuladores configurados para responder a eventos manipulados são invocados. Basicamente, marcar um evento roteado como manipulado limita sua visibilidade aos ouvintes ao longo da rota do evento.
Manipuladores de eventos roteados podem ser manipuladores de instância ou de classe. Os controladores de instância manipulam eventos roteados em elementos XAML ou objetos. Os manipuladores de classe manipulam um evento roteado em um nível de classe e são invocados antes que qualquer manipulador de instância responda ao mesmo evento em qualquer instância da classe. Quando os eventos roteados são marcados como manipulados, eles geralmente são marcados como tal dentro de manipuladores de classe. Este artigo discute os benefícios e possíveis armadilhas de marcar eventos roteados como tratados, os diferentes tipos de eventos roteados e manipuladores de eventos roteados e a supressão de eventos em controles compostos.
Pré-requisitos
O artigo pressupõe um conhecimento básico dos eventos roteados e que você leu Visão Geral de Eventos Roteados. Para seguir os exemplos neste artigo, ele ajuda se você estiver familiarizado com XAML (Extensible Application Markup Language) e saber como escrever aplicativos do WPF (Windows Presentation Foundation).
Quando marcar eventos roteados como manipulados
Normalmente, apenas um manipulador deve fornecer uma resposta significativa para cada evento roteado. Evite usar o sistema de eventos roteado para fornecer uma resposta significativa entre vários manipuladores. A definição do que constitui uma resposta significativa é subjetiva e depende do seu aplicativo. Como orientação geral:
- Respostas significativas incluem definir o foco, modificar o estado público, definir propriedades que afetam a representação visual, gerar novos eventos e lidar completamente com um evento.
- Respostas insignificantes incluem modificar o estado privado sem impacto visual ou programático, registro em log de eventos e examinar dados de eventos sem responder ao evento.
Alguns controles do WPF suprimem eventos de nível de componente que não precisam de tratamento adicional marcando-os como manipulados. Se você quiser manipular um evento marcado como manipulado por um controle, consulte Como contornar a supressão de eventos por controles.
Para marcar um evento como manipulado, defina o valor da Handled propriedade em seus dados de evento como true. Embora seja possível reverter esse valor para false, a necessidade de fazê-lo deve ser rara.
Visualização e propagação de pares de eventos roteados
Os pares de eventos de pré-visualização e de propagação são específicos para eventos de entrada. Vários eventos de entrada implementam um par de eventos roteados de tunelamento e borbulhamento, como e . O prefixo Preview significa que o evento de propagação é iniciado após a conclusão do evento de visualização. Cada par de eventos de pré-visualização e propagação compartilha a mesma instância de dados de evento.
Manipuladores de eventos roteados são invocados em uma ordem que corresponde a estratégia de roteamento de um evento.
- O evento de visualização percorre desde o elemento raiz do aplicativo até o elemento que gerou o evento roteado. Os manipuladores de eventos de visualização anexados ao elemento raiz do aplicativo são invocados primeiro, seguidos por manipuladores anexados a elementos aninhados sucessivos.
- Após a conclusão do evento de visualização, o evento de borbulha emparelhamento viaja do elemento que gerou o evento roteado para o elemento raiz do aplicativo. Manipuladores de eventos de bolhas anexados ao mesmo elemento que gerou o evento roteado são invocados primeiro, seguidos por manipuladores anexados a elementos pai sucessivos.
Eventos de visualização e de propagação emparelhados fazem parte da implementação interna de várias classes do WPF que declaram e geram seus próprios eventos roteados. Sem essa implementação interna de nível de classe, os eventos roteado de visualização e de bolhas são totalmente separados e não compartilharão dados de evento, independentemente da nomenclatura do evento. Para obter informações sobre como implementar eventos roteados de entrada do tipo túnel ou borbulha em uma classe personalizada, consulte Criar um evento roteado personalizado.
Como cada par de eventos de visualização e borbulhante compartilha a mesma instância de dados de evento, se um evento roteado de visualização for marcado como manipulado, seu evento de borbulhamento emparelhado também será tratado. Se um evento roteado borbulhante for marcado como manipulado, ele não afetará o evento de visualização emparelhado porque o evento de visualização foi concluído. Tenha cuidado ao marcar os pares de eventos de entrada de visualização e de visualização conforme tratado. Um evento de pré-visualização manipulado não invocará manipuladores de eventos normalmente registrados para o restante da rota de tunelamento, e o evento de propagação emparelhado não será gerado. Um evento de entrada borbulhante manipulado não invocará manipuladores de eventos normalmente registrados para o restante da rota de borbulha.
Manipuladores de eventos roteado de instância e classe
Os manipuladores de eventos roteados podem ser manipuladores de instância ou de classe. Manipuladores de classe para uma determinada classe são invocados antes que qualquer manipulador de instância responda ao mesmo evento em qualquer instância dessa classe. Devido a esse comportamento, quando os eventos roteados são marcados como manipulados, eles geralmente são marcados dessa forma dentro de manipuladores de classe. Há dois tipos de manipuladores de classe:
- Manipuladores de eventos de classe estática, que são registrados chamando o RegisterClassHandler método dentro de um construtor de classe estática.
- Substitua manipuladores de eventos de classe, que são registrados substituindo métodos de evento virtual de classe base. Os métodos de evento virtual de classe base existem principalmente para eventos de entrada e têm nomes que começam com o nome< do evento On> e o nome< do evento OnPreview>.
Manipuladores de eventos de instância
Você pode anexar manipuladores de instância a objetos ou elementos XAML chamando diretamente o AddHandler método. Os eventos roteados do WPF implementam um wrapper de evento CLR (Tempo de Execução Comum) que usa o método AddHandler para anexar manipuladores de eventos. Dado que a sintaxe de atributos XAML para anexar manipuladores de eventos resulta em uma chamada ao encapsulador de eventos CLR, mesmo ao anexar manipuladores no XAML, isso se resolve em uma chamada AddHandler. Para eventos tratados:
- Manipuladores anexados usando a sintaxe de atributo XAML ou a assinatura comum
AddHandlernão são invocados. - Manipuladores que são anexados usando a sobrecarga AddHandler(RoutedEvent, Delegate, Boolean) com o parâmetro
handledEventsToodefinido paratruesão invocados. Essa sobrecarga está disponível para os casos raros quando é necessário responder a eventos processados. Por exemplo, algum elemento em uma árvore de elementos marcou um evento como manipulado, mas outros elementos ao longo da rota de evento precisam responder ao evento manipulado.
O exemplo XAML a seguir adiciona um controle personalizado chamado componentWrapper, que encapsula um TextBox nomeado componentTextBox, a um StackPanel chamado outerStackPanel. Um manipulador de eventos de instância para o evento PreviewKeyDown é anexado a componentWrapper usando a sintaxe de atributo XAML. Como resultado, o manipulador de instâncias só responderá a eventos de tunelamento não tratados PreviewKeyDown gerados pelo componentTextBox.
<StackPanel Name="outerStackPanel" VerticalAlignment="Center">
<custom:ComponentWrapper
x:Name="componentWrapper"
TextBox.PreviewKeyDown="HandlerInstanceEventInfo"
HorizontalAlignment="Center">
<TextBox Name="componentTextBox" Width="200" />
</custom:ComponentWrapper>
</StackPanel>
O MainWindow construtor anexa um manipulador de instância para o evento de bolhas KeyDown ao componentWrapper usando a sobrecarga UIElement.AddHandler(RoutedEvent, Delegate, Boolean), com o parâmetro handledEventsToo definido como true. Como resultado, o manipulador de eventos de instância responderá a eventos sem tratamento e manipulados.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Attach an instance handler on componentWrapper that will be invoked by handled KeyDown events.
componentWrapper.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler.InstanceEventInfo),
handledEventsToo: true);
}
// The handler attached to componentWrapper in XAML.
public void HandlerInstanceEventInfo(object sender, KeyEventArgs e) =>
Handler.InstanceEventInfo(sender, e);
}
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
' Attach an instance handler on componentWrapper that will be invoked by handled KeyDown events.
componentWrapper.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf InstanceEventInfo),
handledEventsToo:=True)
End Sub
' The handler attached to componentWrapper in XAML.
Public Sub HandlerInstanceEventInfo(sender As Object, e As KeyEventArgs)
InstanceEventInfo(sender, e)
End Sub
End Class
A implementação de ComponentWrapper é mostrada na próxima seção.
Manipuladores de eventos de classe estática
Você pode anexar manipuladores de eventos de classe estática chamando o RegisterClassHandler método no construtor estático de uma classe. Cada classe em uma hierarquia de classes pode registrar seu próprio manipulador de classe estático para cada evento roteado. Como resultado, pode haver vários manipuladores de classe estáticos invocados para o mesmo evento em qualquer nó determinado na rota do evento. Quando a rota de evento para o evento é construída, todos os manipuladores de classe estáticos para cada nó são adicionados à rota do evento. A ordem de invocação de manipuladores de classe estática em um nó começa com o manipulador de classe estática mais derivado, seguido por manipuladores de classe estáticos de cada classe base sucessiva.
Os manipuladores de eventos de classe estática registrados usando a RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) sobrecarga com o handledEventsToo parâmetro definido para true responderão a eventos roteados sem tratamento e manipulados.
Manipuladores de classe estáticos normalmente são registrados para responder somente a eventos sem tratamento. Nesse caso, se um manipulador de classe derivada em um nó marcar um evento como manipulado, os manipuladores de classe base para esse evento não serão invocados. Nesse cenário, o manipulador de classe base é efetivamente substituído pelo manipulador de classe derivada. Os manipuladores de classe base geralmente contribuem para controlar o design em áreas como aparência visual, lógica de estado, manipulação de entrada e manipulação de comandos, portanto, tenha cuidado ao substituí-los. Manipuladores de classe derivadas que não marcam um evento como manipulado acabam complementando os manipuladores de classe base em vez de substituí-los.
O exemplo de código a seguir mostra a hierarquia de classe para o ComponentWrapper controle personalizado que foi referenciado no XAML anterior. A ComponentWrapper classe deriva da ComponentWrapperBase classe, que, por sua vez, deriva da StackPanel classe. O método RegisterClassHandler, usado no construtor estático das classes ComponentWrapper e ComponentWrapperBase, registra um manipulador de eventos estático de classe para cada uma dessas classes. O sistema de eventos WPF invoca o ComponentWrapper manipulador de classe estático antes do ComponentWrapperBase manipulador de classe estático.
public class ComponentWrapper : ComponentWrapperBase
{
static ComponentWrapper()
{
// Class event handler implemented in the static constructor.
EventManager.RegisterClassHandler(typeof(ComponentWrapper), KeyDownEvent,
new RoutedEventHandler(Handler.ClassEventInfo_Static));
}
// Class event handler that overrides a base class virtual method.
protected override void OnKeyDown(KeyEventArgs e)
{
Handler.ClassEventInfo_Override(this, e);
// Call the base OnKeyDown implementation on ComponentWrapperBase.
base.OnKeyDown(e);
}
}
public class ComponentWrapperBase : StackPanel
{
// Class event handler implemented in the static constructor.
static ComponentWrapperBase()
{
EventManager.RegisterClassHandler(typeof(ComponentWrapperBase), KeyDownEvent,
new RoutedEventHandler(Handler.ClassEventInfoBase_Static));
}
// Class event handler that overrides a base class virtual method.
protected override void OnKeyDown(KeyEventArgs e)
{
Handler.ClassEventInfoBase_Override(this, e);
e.Handled = true;
Debug.WriteLine("The KeyDown routed event is marked as handled.");
// Call the base OnKeyDown implementation on StackPanel.
base.OnKeyDown(e);
}
}
Public Class ComponentWrapper
Inherits ComponentWrapperBase
Shared Sub New()
' Class event handler implemented in the static constructor.
EventManager.RegisterClassHandler(GetType(ComponentWrapper), KeyDownEvent,
New RoutedEventHandler(AddressOf ClassEventInfo_Static))
End Sub
' Class event handler that overrides a base class virtual method.
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
ClassEventInfo_Override(Me, e)
' Call the base OnKeyDown implementation on ComponentWrapperBase.
MyBase.OnKeyDown(e)
End Sub
End Class
Public Class ComponentWrapperBase
Inherits StackPanel
Shared Sub New()
' Class event handler implemented in the static constructor.
EventManager.RegisterClassHandler(GetType(ComponentWrapperBase), KeyDownEvent,
New RoutedEventHandler(AddressOf ClassEventInfoBase_Static))
End Sub
' Class event handler that overrides a base class virtual method.
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
ClassEventInfoBase_Override(Me, e)
e.Handled = True
Debug.WriteLine("The KeyDown event is marked as handled.")
' Call the base OnKeyDown implementation on StackPanel.
MyBase.OnKeyDown(e)
End Sub
End Class
A implementação "code-behind" dos manipuladores de eventos da classe de sobrecarga neste exemplo de código é discutida na próxima seção.
Sobrescrever manipuladores de eventos de classe
Algumas classes base de elementos visuais expõem o nome< do evento Vazio> e os métodos virtuais de nome< de evento OnPreview> para cada um de seus eventos de entrada roteado públicos. Por exemplo, UIElement implementa os OnKeyDown manipuladores de eventos virtuais e OnPreviewKeyDown muitos outros. Você pode substituir manipuladores de eventos virtuais de classe base para implementar manipuladores de eventos de classe de substituição para suas classes derivadas. Por exemplo, você pode adicionar um manipulador de classe para sobrescrever o evento DragEnter em qualquer classe derivada de UIElement substituindo o método virtual OnDragEnter. Substituir métodos virtuais de classe base é uma maneira mais simples de implementar manipuladores de classe do que registrar manipuladores de classe em um construtor estático. Na substituição, você pode gerar eventos, iniciar a lógica específica da classe para alterar as propriedades do elemento em instâncias, marcar o evento como manipulado ou executar outra lógica de tratamento de eventos.
Ao contrário dos manipuladores de eventos de classe estática, o sistema de eventos WPF invoca apenas manipuladores de eventos de classe de substituição para a classe mais derivada em uma hierarquia de classe. A classe mais derivada em uma hierarquia de classe pode usar a palavra-chave base para chamar a implementação base do método virtual. Na maioria das vezes, você deve chamar a implementação base, independentemente de você ter marcado um evento como manipulado. Você só deve omitir chamar a implementação base se sua classe tiver um requisito para substituir a lógica de implementação base, se houver. Se você chamar a implementação base antes ou depois do código de substituição depende da natureza de sua implementação.
No exemplo de código anterior, o método virtual da classe OnKeyDown base é sobrescrito nas classes ComponentWrapper e ComponentWrapperBase. Como o sistema de eventos do WPF invoca apenas o ComponentWrapper.OnKeyDown manipulador de eventos de classe de substituição, esse manipulador usa base.OnKeyDown(e) para chamar o ComponentWrapperBase.OnKeyDown manipulador de eventos de classe de substituição, que, por sua vez, usa base.OnKeyDown(e) para chamar o StackPanel.OnKeyDown método virtual. A ordem dos eventos no exemplo de código anterior é:
- O manipulador de instância anexado a
componentWrapperé disparado pelo evento roteadoPreviewKeyDown. - O manipulador de classe estático anexado a
componentWrapperé disparado pelo evento roteadoKeyDown. - O manipulador de classe estático anexado a
componentWrapperBaseé disparado pelo evento roteadoKeyDown. - O manipulador de classe de substituição anexado a
componentWrapperé disparado pelo evento roteadoKeyDown. - O manipulador de classe de substituição anexado a
componentWrapperBaseé disparado pelo evento roteadoKeyDown. - O
KeyDownevento roteado é marcado como manipulado. - O manipulador de instância anexado a
componentWrapperé disparado pelo evento roteadoKeyDown. O manipulador foi registrado com ohandledEventsTooparâmetro definido comotrue.
Supressão de evento de entrada em controles compostos
Alguns controles compostos suprimem eventos de entrada no nível do componente para substituí-los por um evento personalizado de alto nível que carrega mais informações ou implica um comportamento mais específico. Um controle composto é composto por definição de vários controles práticos ou classes base de controle. Um exemplo clássico é o Button controle, que transforma vários eventos de mouse em um Click evento roteado. A Button classe base é ButtonBase, que deriva indiretamente de UIElement. Grande parte da infraestrutura de eventos necessária para o processamento de entrada de controle está disponível no nível UIElement.
UIElement expõe vários Mouse eventos, como MouseLeftButtonDown e MouseRightButtonDown.
UIElement também implementa os métodos virtuais vazios OnMouseLeftButtonDown e OnMouseRightButtonDown como manipuladores de classe pré-registrados.
ButtonBase substitui esses manipuladores de classe, e, dentro do manipulador de substituição, define a propriedade Handled para true e dispara um evento Click. O resultado final para a maioria dos ouvintes é que os eventos MouseLeftButtonDown e MouseRightButtonDown estão ocultos, e o evento de alto nível Click está visível.
Trabalhando em torno da supressão de eventos de entrada
Às vezes, a supressão de eventos em controles individuais pode interferir na lógica de tratamento de eventos em seu aplicativo. Por exemplo, se seu aplicativo usou a sintaxe de atributo XAML para anexar um manipulador para o MouseLeftButtonDown evento no elemento raiz XAML, esse manipulador não será invocado porque o Button controle marca o MouseLeftButtonDown evento como manipulado. Se você quiser que os elementos em direção à raiz do aplicativo sejam chamados para um evento roteado já tratado, você pode fazer o seguinte:
Anexe manipuladores chamando o método UIElement.AddHandler(RoutedEvent, Delegate, Boolean) com o parâmetro
handledEventsToodefinido comotrue. Essa abordagem requer anexar o manipulador de eventos no code-behind, depois de obter uma referência de objeto para o elemento ao qual ele será anexado.Se o evento marcado como manipulado for um evento de entrada borbulhante, anexe manipuladores para o evento de visualização emparelhado, se disponível. Por exemplo, se um controle suprimir o
MouseLeftButtonDownevento, você poderá anexar um manipulador para o PreviewMouseLeftButtonDown evento. Essa abordagem só funciona para pares de eventos de entrada de visualização e de bolhas, que compartilham dados de eventos. Tenha cuidado para não marcar oPreviewMouseLeftButtonDowncomo processado, pois isso suprimiria completamente os eventos Click.
Para obter um exemplo de como contornar a supressão de eventos de entrada, consulte Como contornar a supressão de eventos por controles.
Consulte também
.NET Desktop feedback