Eventos de visualização (WPF .NET)
Os eventos de visualização, também conhecidos como eventos de túnel, são eventos roteados que percorrem a árvore de elementos do elemento raiz do aplicativo para o elemento que gerou o evento. O elemento que gera um evento é relatado como o Source nos dados do evento. Nem todos os cenários de eventos dão suporte ou exigem eventos de visualização. Este artigo descreve onde existem eventos de visualização e como os aplicativos ou componentes podem interagir com eles. Para obter informações sobre como criar um evento de visualização, consulte Como criar um evento roteado personalizado.
Pré-requisitos
O artigo pressupõe um conhecimento básico de eventos roteados e que você leu Visão geral de eventos roteados. Para seguir os exemplos neste artigo, é útil se você estiver familiarizado com XAML (Extensible Application Markup Language) e souber como escrever aplicativos WPF (Windows Presentation Foundation).
Visualizar eventos marcados como manipulados
Tenha cuidado ao marcar eventos de visualização como manipulados nos dados do evento. Marcar um evento de visualização como manipulado em um elemento diferente do elemento que o gerou pode impedir que o elemento que o gerou manipule o evento. Às vezes, marcar eventos de visualização como manipulados é intencional. Por exemplo, um controle composto pode suprimir eventos gerados por componentes individuais e substituí-los por eventos gerados pelo controle completo. Os eventos personalizados para um controle podem fornecer dados de evento personalizados e disparar com base nas relações de estado do componente.
Para eventos de entrada, os dados do evento são compartilhados pelos equivalentes de visualização e não visualização (propagação) de cada evento. Se você usar um manipulador de classe de evento de visualização para marcar um evento de entrada como manipulado, os manipuladores de classe para o evento de entrada de propagação normalmente não serão invocados. Ou, se você usar um manipulador de instâncias de evento de visualização para marcar um evento como manipulado, os manipuladores de instâncias para o evento de entrada de propagação normalmente não serão invocados. Embora você possa configurar manipuladores de classe e instância para serem invocados mesmo que um evento seja marcado como manipulado, essa configuração de manipulador não é comum. Para obter mais informações sobre a manipulação de classe e como ela se relaciona com eventos de visualização, consulte Marcando eventos roteados como manipulados e manipulação de classe.
Observação
Nem todos os eventos de visualização são eventos de túnel. Por exemplo, o PreviewMouseLeftButtonDown evento de entrada segue uma rota descendente por meio da árvore de elementos, mas é um evento roteado direto que é gerado e regerado por cada um UIElement na rota.
Trabalhando em torno da supressão de eventos por controles
Alguns controles compostos suprimem eventos de entrada no nível do componente para substituí-los por um evento de alto nível personalizado. Por exemplo, o WPF ButtonBase marca o evento de entrada de propagação como manipulado MouseLeftButtonDown em seu OnMouseLeftButtonDown método e gera o Click evento. O MouseLeftButtonDown
evento e seus dados de evento ainda continuam ao longo da rota da árvore de elementos, mas como o evento é marcado como Handled em dados de evento, somente os manipuladores configurados para responder a eventos manipulados são chamados.
Se você quiser que outros elementos em direção à raiz do seu aplicativo manipulem um evento roteado marcado como manipulado, você pode:
Anexe manipuladores chamando o UIElement.AddHandler(RoutedEvent, Delegate, Boolean) método e definindo o parâmetro
handledEventsToo
comotrue
. Essa abordagem requer anexar o manipulador de eventos no code-behind, depois de obter uma referência de objeto ao elemento ao qual ele será anexado.Se o evento marcado como manipulado for um evento de propagação, anexe manipuladores para o evento de visualização equivalente, se disponível. Por exemplo, se um controle suprimir o MouseLeftButtonDown evento, você poderá anexar um manipulador para o PreviewMouseLeftButtonDown evento. Essa abordagem só funciona para eventos de entrada de elemento base que implementam estratégias de roteamento de tunelamento e bolha e compartilham dados de eventos.
O exemplo a seguir implementa um controle personalizado rudimentar chamado componentWrapper
que contém um TextBox. O controle é adicionado a um StackPanel .outerStackPanel
<StackPanel Name="outerStackPanel"
VerticalAlignment="Center"
custom:ComponentWrapper.CustomKey="Handler_PrintEventInfo"
TextBox.KeyDown="Handler_PrintEventInfo"
TextBox.PreviewKeyDown="Handler_PrintEventInfo" >
<custom:ComponentWrapper
x:Name="componentWrapper"
TextBox.KeyDown="ComponentWrapper_KeyDown"
custom:ComponentWrapper.CustomKey="Handler_PrintEventInfo"
HorizontalAlignment="Center">
<TextBox Name="componentTextBox" Width="200" KeyDown="Handler_PrintEventInfo" />
</custom:ComponentWrapper>
</StackPanel>
O componentWrapper
controle escuta o KeyDown evento de propagação gerado por seu TextBox
componente sempre que ocorre um pressionamento de tecla. Nessa ocorrência, o componentWrapper
controle:
Marca o evento roteado de propagação como manipulado
KeyDown
para suprimi-lo. Como resultado, somente oouterStackPanel
manipulador configurado no code-behind para responder a eventos manipuladosKeyDown
é disparado. OouterStackPanel
manipulador anexado em XAML paraKeyDown
eventos não é invocado.Gera um evento roteado de propagação personalizado chamado
CustomKey
, que aciona oouterStackPanel
manipulador para oCustomKey
evento.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Attach a handler on outerStackPanel that will be invoked by handled KeyDown events.
outerStackPanel.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler_PrintEventInfo),
handledEventsToo: true);
}
private void ComponentWrapper_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
Handler_PrintEventInfo(sender, e);
Debug.WriteLine("KeyDown event marked as handled on componentWrapper.\r\n" +
"CustomKey event raised on componentWrapper.");
// Mark the event as handled.
e.Handled = true;
// Raise the custom click event.
componentWrapper.RaiseCustomRoutedEvent();
}
private void Handler_PrintEventInfo(object sender, System.Windows.Input.KeyEventArgs e)
{
string senderName = ((FrameworkElement)sender).Name;
string sourceName = ((FrameworkElement)e.Source).Name;
string eventName = e.RoutedEvent.Name;
string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : "";
Debug.WriteLine($"Handler attached to {senderName} " +
$"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}");
}
private void Handler_PrintEventInfo(object sender, RoutedEventArgs e)
{
string senderName = ((FrameworkElement)sender).Name;
string sourceName = ((FrameworkElement)e.Source).Name;
string eventName = e.RoutedEvent.Name;
string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : "";
Debug.WriteLine($"Handler attached to {senderName} " +
$"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}");
}
// Debug output:
//
// Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox.
// Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox.
// Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox.
// KeyDown event marked as handled on componentWrapper.
// CustomKey event raised on componentWrapper.
// Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper.
// Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper.
// Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true.
}
public class ComponentWrapper : StackPanel
{
// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent CustomKeyEvent =
EventManager.RegisterRoutedEvent(
name: "CustomKey",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(ComponentWrapper));
// Provide CLR accessors for assigning an event handler.
public event RoutedEventHandler CustomKey
{
add { AddHandler(CustomKeyEvent, value); }
remove { RemoveHandler(CustomKeyEvent, value); }
}
public void RaiseCustomRoutedEvent()
{
// Create a RoutedEventArgs instance.
RoutedEventArgs routedEventArgs = new(routedEvent: CustomKeyEvent);
// Raise the event, which will bubble up through the element tree.
RaiseEvent(routedEventArgs);
}
}
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
' Attach a handler on outerStackPanel that will be invoked by handled KeyDown events.
outerStackPanel.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf Handler_PrintEventInfo),
handledEventsToo:=True)
End Sub
Private Sub ComponentWrapper_KeyDown(sender As Object, e As KeyEventArgs)
Handler_PrintEventInfo(sender, e)
Debug.WriteLine("KeyDown event marked as handled on componentWrapper." &
vbCrLf & "CustomKey event raised on componentWrapper.")
' Mark the event as handled.
e.Handled = True
' Raise the custom click event.
componentWrapper.RaiseCustomRoutedEvent()
End Sub
Private Sub Handler_PrintEventInfo(sender As Object, e As KeyEventArgs)
Dim senderName As String = CType(sender, FrameworkElement).Name
Dim sourceName As String = CType(e.Source, FrameworkElement).Name
Dim eventName As String = e.RoutedEvent.Name
Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "")
Debug.WriteLine($"Handler attached to {senderName} " &
$"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}")
End Sub
Private Sub Handler_PrintEventInfo(sender As Object, e As RoutedEventArgs)
Dim senderName As String = CType(sender, FrameworkElement).Name
Dim sourceName As String = CType(e.Source, FrameworkElement).Name
Dim eventName As String = e.RoutedEvent.Name
Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "")
Debug.WriteLine($"Handler attached to {senderName} " &
$"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}")
End Sub
' Debug output
'
' Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox.
' Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox.
' Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox.
' KeyDown event marked as handled on componentWrapper.
' CustomKey event raised on componentWrapper.
' Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper.
' Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper.
' Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true.
End Class
Public Class ComponentWrapper
Inherits StackPanel
' Register a custom routed event with the Bubble routing strategy.
Public Shared ReadOnly CustomKeyEvent As RoutedEvent =
EventManager.RegisterRoutedEvent(
name:="CustomKey",
routingStrategy:=RoutingStrategy.Bubble,
handlerType:=GetType(RoutedEventHandler),
ownerType:=GetType(ComponentWrapper))
' Provide CLR accessors to support event handler assignment.
Public Custom Event CustomKey As RoutedEventHandler
AddHandler(value As RoutedEventHandler)
[AddHandler](CustomKeyEvent, value)
End AddHandler
RemoveHandler(value As RoutedEventHandler)
[RemoveHandler](CustomKeyEvent, value)
End RemoveHandler
RaiseEvent(sender As Object, e As RoutedEventArgs)
[RaiseEvent](e)
End RaiseEvent
End Event
Public Sub RaiseCustomRoutedEvent()
' Create a RoutedEventArgs instance & raise the event,
' which will bubble up through the element tree.
Dim routedEventArgs As New RoutedEventArgs(routedEvent:=CustomKeyEvent)
[RaiseEvent](routedEventArgs)
End Sub
End Class
O exemplo demonstra duas soluções alternativas para fazer com que o evento roteado suprimido KeyDown
invoque um manipulador de eventos anexado outerStackPanel
ao :
Anexe um PreviewKeyDown manipulador de eventos ao
outerStackPanel
. Como um evento roteado de entrada de visualização precede o evento roteado de propagação equivalente, oPreviewKeyDown
manipulador no exemplo é executado à frente doKeyDown
manipulador que suprime os eventos de visualização e propagação por meio de seus dados de evento compartilhados.Anexe um
KeyDown
manipulador de eventos aoouterStackPanel
usando o UIElement.AddHandler(RoutedEvent, Delegate, Boolean) método no code-behind, com ohandledEventsToo
parâmetro definido comotrue
.
Observação
Marcar equivalentes de visualização ou não visualização de eventos de entrada como manipulados são estratégias para suprimir eventos gerados pelos componentes de um controle. A abordagem que você usa depende dos requisitos do seu aplicativo.
Confira também
.NET Desktop feedback