Événements en préversion (WPF .NET)
Les événements d’aperçu, également appelés événements de tunneling, sont des événements routés qui transitent vers le bas par le biais de l’arborescence d’éléments de l’élément racine de l’application vers l’élément qui a déclenché l’événement. L’élément qui déclenche un événement est signalé comme étant dans Source les données d’événement. Tous les scénarios d’événements ne prennent pas en charge ou nécessitent des événements en préversion. Cet article décrit l’emplacement des événements en préversion et la façon dont les applications ou les composants peuvent interagir avec eux. Pour plus d’informations sur la création d’un événement en préversion, consultez Comment créer un événement routé personnalisé.
Prérequis
L’article suppose une connaissance de base des événements routés et que vous avez lu la vue d’ensemble des événements routés. Pour suivre les exemples de cet article, il vous aide à connaître le langage XAML (Extensible Application Markup Language) et savoir comment écrire des applications Windows Presentation Foundation (WPF).
Événements en préversion marqués comme gérés
Soyez prudent lorsque vous marquez les événements d’aperçu comme gérés dans les données d’événement. Marquer un événement d’aperçu comme géré sur un élément autre que l’élément qui l’a déclenché peut empêcher l’élément qui l’a déclenché de gérer l’événement. Parfois, le marquage des événements d’aperçu comme gérés est intentionnel. Par exemple, un contrôle composite peut supprimer les événements déclenchés par des composants individuels et les remplacer par des événements déclenchés par le contrôle complet. Les événements personnalisés pour un contrôle peuvent fournir des données d’événement personnalisées et un déclencheur en fonction des relations d’état des composants.
Pour les événements d’entrée, les données d’événement sont partagées par les équivalents de préversion et de non-préversion (bubbling) de chaque événement. Si vous utilisez un gestionnaire de classes d’événements en préversion pour marquer un événement d’entrée comme géré, les gestionnaires de classes pour l’événement d’entrée de basculement ne seront généralement pas appelés. Sinon, si vous utilisez un gestionnaire d’instances d’événement en préversion pour marquer un événement comme géré, les gestionnaires d’instances pour l’événement d’entrée en cours d’envoi ne seront généralement pas appelés. Bien que vous puissiez configurer des gestionnaires de classes et d’instances à appeler même si un événement est marqué comme géré, cette configuration de gestionnaire n’est pas courante. Pour plus d’informations sur la gestion des classes et leur relation avec les événements d’aperçu, consultez Marquage des événements routés comme gérés et gestion des classes.
Remarque
Tous les événements d’aperçu ne sont pas des événements de tunneling . Par exemple, l’événement PreviewMouseLeftButtonDown d’entrée suit un itinéraire vers le bas par le biais de l’arborescence d’éléments, mais il s’agit d’un événement routé direct déclenché et réexéché par chacun UIElement dans l’itinéraire.
Contourner la suppression des événements par les contrôles
Certains contrôles composites suppriment les événements d’entrée au niveau du composant afin de les remplacer par un événement de haut niveau personnalisé. Par exemple, WPF ButtonBase marque l’événement MouseLeftButtonDown d’entrée de bouclage comme géré dans sa OnMouseLeftButtonDown méthode et déclenche l’événement Click . L’événement MouseLeftButtonDown
et ses données d’événement continuent toujours le long de l’itinéraire de l’arborescence des éléments, mais, étant donné que l’événement est marqué comme Handled étant dans les données d’événement, seuls les gestionnaires configurés pour répondre aux événements gérés sont appelés.
Si vous souhaitez que d’autres éléments vers la racine de votre application gèrent un événement routé marqué comme géré, vous pouvez :
Attachez des gestionnaires en appelant la UIElement.AddHandler(RoutedEvent, Delegate, Boolean) méthode et en définissant le paramètre
handledEventsToo
surtrue
. Cette approche nécessite l’attachement du gestionnaire d’événements dans code-behind, après avoir obtenu une référence d’objet à l’élément auquel il sera attaché.Si l’événement marqué comme géré est un événement de bulle, attachez des gestionnaires pour l’événement d’aperçu équivalent si disponible. Par exemple, si un contrôle supprime l’événement MouseLeftButtonDown , vous pouvez attacher un gestionnaire pour l’événement à la PreviewMouseLeftButtonDown place. Cette approche fonctionne uniquement pour les événements d’entrée d’élément de base qui implémentent à la fois des stratégies de tunneling et de routage de bubbling et partagent des données d’événement.
L’exemple suivant implémente un contrôle personnalisé rudimentaire nommé componentWrapper
qui contient un TextBox. Le contrôle est ajouté à un StackPanel nom 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>
Le componentWrapper
contrôle écoute l’événement KeyDown de boublage déclenché par son TextBox
composant chaque fois qu’une séquence de touches se produit. Sur cette occurrence, le componentWrapper
contrôle :
Marque l’événement
KeyDown
routé de bouclage comme géré pour le supprimer. Par conséquent, seul leouterStackPanel
gestionnaire configuré dans le code-behind pour répondre aux événements gérésKeyDown
est déclenché. LeouterStackPanel
gestionnaire attaché en XAML pourKeyDown
les événements n’est pas appelé.Déclenche un événement routé personnalisé nommé
CustomKey
, qui déclenche leouterStackPanel
gestionnaire de l’événementCustomKey
.
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
L’exemple illustre deux solutions de contournement pour obtenir l’événement routé KeyDown
supprimé pour appeler un gestionnaire d’événements attaché au outerStackPanel
:
Attachez un gestionnaire d’événements PreviewKeyDown au
outerStackPanel
. Étant donné qu’un événement routé d’entrée en préversion précède l’événement routé de bubbling équivalent, lePreviewKeyDown
gestionnaire dans l’exemple s’exécute devant leKeyDown
gestionnaire qui supprime à la fois les événements d’aperçu et de bouclage par le biais de leurs données d’événements partagées.Attachez un gestionnaire d’événements
KeyDown
à l’aideouterStackPanel
de la UIElement.AddHandler(RoutedEvent, Delegate, Boolean) méthode dans code-behind, avec lehandledEventsToo
paramètre défini surtrue
.
Remarque
Le marquage de l’aperçu ou des équivalents non préversion des événements d’entrée comme gérés sont les deux stratégies pour supprimer les événements déclenchés par les composants d’un contrôle. L’approche que vous utilisez dépend des exigences de votre application.
Voir aussi
.NET Desktop feedback