Preview-gebeurtenissen (WPF .NET)
Preview-gebeurtenissen, ook wel tunnelinggebeurtenissen genoemd, zijn gerouteerde gebeurtenissen die omlaag door de elementstructuur van het hoofdelement van de toepassing lopen naar het element dat de gebeurtenis heeft gegenereerd. Het element dat een gebeurtenis genereert, wordt gerapporteerd als de Source in de gebeurtenisgegevens. Niet alle gebeurtenisscenario's ondersteunen of vereisen voorvertoningsgebeurtenissen. In dit artikel wordt beschreven waar preview-gebeurtenissen bestaan en hoe toepassingen of onderdelen ermee kunnen communiceren. Raadpleeg Hoe een aangepaste gerouteerde gebeurtenis te makenvoor meer informatie over het maken van een preview-evenement.
Voorwaarden
In het artikel wordt ervan uitgegaan dat u basiskennis hebt van gerouteerde gebeurtenissen en dat u overzicht van gerouteerde gebeurtenissen hebt gelezen. Als u de voorbeelden in dit artikel wilt volgen, helpt dit als u bekend bent met Extensible Application Markup Language (XAML) en weet hoe u WPF-toepassingen (Windows Presentation Foundation) schrijft.
Voer een voorbeeldweergave uit van gebeurtenissen die als afgehandeld zijn gemarkeerd.
Wees voorzichtig bij het markeren van preview-gebeurtenissen zoals verwerkt in gebeurtenisgegevens. Als u een voorbeeldgebeurtenis markeert als afgehandeld op een ander element dan het element dat het heeft gegenereerd, kan dit verhinderen dat het element dat de gebeurtenis heeft opgewekt, de gebeurtenis afhandelt. Soms is het markeren van preview-gebeurtenissen zoals verwerkt opzettelijk. Een samengesteld besturingselement kan bijvoorbeeld gebeurtenissen onderdrukken die door afzonderlijke onderdelen worden gegenereerd en vervangen door gebeurtenissen die door het volledige besturingselement worden gegenereerd. Aangepaste gebeurtenissen voor een besturingselement kunnen aangepaste gebeurtenisgegevens en triggers bieden op basis van statusrelaties van onderdelen.
Voor invoergebeurtenissenworden gebeurtenisgegevens gedeeld door zowel de preview- als niet-preview-equivalenten (bubbling) van elke gebeurtenis. Als u een preview-gebeurtenisklasse-handler gebruikt om een invoergebeurtenis als afgehandeld te markeren, worden klasse-handlers voor de invoergebeurtenis doorgaans niet uitgevoerd. Als u een voorbeeldhandler gebruikt om een gebeurtenis als verwerkt te markeren, worden handlers voor de bubbelende invoergebeurtenis doorgaans niet aangeroepen. Hoewel u klasse- en exemplaarhandlers kunt configureren om te worden aangeroepen, zelfs als een gebeurtenis is gemarkeerd als afgehandeld, is die handlerconfiguratie niet gebruikelijk. Zie Gerouteerde gebeurtenissen markeren als verwerkt en klasseafhandelingvoor meer informatie over het afhandelen van klassen en hoe deze betrekking heeft op preview-gebeurtenissen.
Notitie
Niet alle preview-gebeurtenissen zijn tunneling gebeurtenissen. De PreviewMouseLeftButtonDown invoergebeurtenis volgt bijvoorbeeld een neerwaartse route door de elementenboom, maar is een directe gerouteerde gebeurtenis die door elke UIElement in de route wordt opgeroepen en opnieuw aangeroepen.
Het omzeilen van gebeurtenisonderdrukking door bedieningselementen
Sommige samengestelde besturingselementen onderdrukken invoergebeurtenissen op onderdeelniveau om deze te vervangen door een aangepaste gebeurtenis op hoog niveau. De WPF-ButtonBase markeert bijvoorbeeld de MouseLeftButtonDown-bubbling invoergebeurtenis als verwerkt in de OnMouseLeftButtonDown-methode en veroorzaakt de Click-gebeurtenis. De MouseLeftButtonDown
gebeurtenis en de bijbehorende gebeurtenisgegevens worden nog steeds langs de structuurroute van het element voortgezet, maar omdat de gebeurtenis is gemarkeerd als Handled in gebeurtenisgegevens, worden alleen handlers aangeroepen die zijn geconfigureerd om te reageren op afgehandelde gebeurtenissen.
Als u andere elementen richting de wortel van uw applicatie een gerouteerde gebeurtenis die als afgehandeld gemarkeerd is wilt laten afhandelen, kunt u het volgende overwegen:
Koppel handlers door de methode UIElement.AddHandler(RoutedEvent, Delegate, Boolean) aan te roepen en de parameter
handledEventsToo
in te stellen optrue
. Deze aanpak vereist het koppelen van de gebeurtenis-handler in code-behind, na het verkrijgen van een objectverwijzing naar het element waaraan deze wordt gekoppeld.Als de gebeurtenis die als verwerkt is gemarkeerd een bubblinggebeurtenis is, voegt u handlers toe voor de equivalente preview-gebeurtenis, indien beschikbaar. Als een besturingselement bijvoorbeeld de MouseLeftButtonDown gebeurtenis onderdrukt, kunt u in plaats daarvan een handler koppelen voor de PreviewMouseLeftButtonDown gebeurtenis. Deze benadering werkt alleen voor invoergebeurtenissen van basiselementen die zowel tunneling als routeringsstrategieën implementeren en gebeurtenisgegevens delen.
In het volgende voorbeeld wordt een elementair aangepast besturingselement met de naam componentWrapper
geïmplementeerd dat een TextBoxbevat. Het besturingselement wordt toegevoegd aan een StackPanel met de naam 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>
Het componentWrapper
besturingselement luistert naar de KeyDown bubbling gebeurtenis die wordt gegenereerd door het TextBox
onderdeel wanneer een toetsaanslag plaatsvindt. Bij die gelegenheid is het besturingselement componentWrapper
:
Markeert de
KeyDown
-bubbelende route-gebeurtenis als afgehandeld om deze te onderdrukken. Als gevolg hiervan wordt alleen deouterStackPanel
-handler die in de code-behind is geconfigureerd om te reageren op -afgehandeldeKeyDown
-gebeurtenissen geactiveerd. DeouterStackPanel
handler die is gekoppeld aan XAML voorKeyDown
gebeurtenissen, wordt niet aangeroepen.Hiermee wordt een aangepaste, gebobbelde gerouteerde gebeurtenis met de naam
CustomKey
gegenereerd, waarmee deouterStackPanel
-handler voor deCustomKey
-gebeurtenis wordt uitgelokt.
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
Het voorbeeld toont twee tijdelijke oplossingen om de onderdrukte KeyDown
gerouteerde gebeurtenis te activeren, zodat een event handler die aan de outerStackPanel
is gekoppeld wordt aangeroepen.
Koppel een PreviewKeyDown event-handler aan de
outerStackPanel
. Omdat een previewinvoergebeurtenis voorafgaat aan de equivalente bubbelende gebeurtenis, wordt in het voorbeeld dePreviewKeyDown
-handler uitgevoerd vóór deKeyDown
-handler, die zowel de preview- als de bubbelende gebeurtenissen onderdrukt met hun gedeelde gebeurtenisgegevens.Koppel een
KeyDown
gebeurtenis-handler aan deouterStackPanel
met behulp van de UIElement.AddHandler(RoutedEvent, Delegate, Boolean) methode in code-behind, waarbij de parameterhandledEventsToo
is ingesteld optrue
.
Notitie
Het markeren van preview- of niet-preview-equivalenten van invoergebeurtenissen zoals verwerkt, zijn beide strategieën voor het onderdrukken van gebeurtenissen die worden gegenereerd door de onderdelen van een besturingselement. De benadering die u gebruikt, is afhankelijk van uw toepassingsvereisten.
Zie ook
.NET Desktop feedback