Förhandsgranskningshändelser (WPF .NET)
Förgranskningsevenemang, även kallade tunnelhändelser, är dirigerade händelser som passerar nedåt genom elementträdet från applikationens rootelement till det element som skapade händelsen. Elementet som genererar en händelse rapporteras som Source i händelsedata. Alla händelsescenarier stöder inte eller kräver förhandsgranskningshändelser. I den här artikeln beskrivs var förhandsgranskningshändelser finns och hur program eller komponenter kan interagera med dem. Information om hur du skapar en förhandsgranskningshändelse finns i Så här skapar du en anpassad dirigerad händelse.
Förutsättningar
Artikeln förutsätter grundläggande kunskaper om routade händelser och att du har läst Översikt över routade händelser. Om du vill följa exemplen i den här artikeln hjälper det om du är bekant med XAML (Extensible Application Markup Language) och vet hur du skriver WPF-program (Windows Presentation Foundation).
Förhandsgranska händelser som markerats som hanterade
Var försiktig när du markerar förhandsgranskningshändelser som hanteras i händelsedata. Om en förhandsgranskningshändelse markeras som hanterad på ett annat element än elementet som utlöste den kan det förhindra att hantera händelsen i elementet som utlöste den. Ibland markeras förhandsgranskningshändelser avsiktligt som hanterade. En sammansatt kontroll kan till exempel förhindra händelser som genereras av enskilda komponenter och ersätta dem med händelser som genereras av den fullständiga kontrollen. Anpassade händelser för en kontroll kan ge anpassade händelsedata och utlösare baserat på komponenttillståndsrelationer.
För indatahändelserdelas händelsedata av både förhandsvisnings- och icke-förhandsvisning (bubblande) ekvivalenter för varje händelse. Om du använder en förhandsgranskningshändelseklasshanterare för att markera en indatahändelse som hanterad anropas vanligtvis inte klasshanterare för den bubblande indatahändelsen. Om du använder en instanshanterare för att förhandsgranska en händelse för att markera den som hanterad, kommer instanshanterare för den bubblande indatahändelsen vanligtvis inte att anropas. Även om du kan konfigurera klass- och instanshanterare som ska anropas även om en händelse markeras som hanterad är den hanterarkonfigurationen inte vanlig. Mer information om klasshantering och hur det relaterar till förhandsgranskningshändelser finns i Märkning av dirigerade händelser som hanterade och klasshantering.
Note
Det är inte alla förhandsversionshändelser som tunneltrafik händelser. Till exempel följer PreviewMouseLeftButtonDown-inndatahändelsen en nedåtgående väg genom elementträdet, men är en direkt dirigerad händelse som initieras och återinitieras av varje UIElement i routen.
Hantera undertryckning av händelser av kontroller
Vissa sammansatta kontroller utelämnar indatahändelser på komponentnivå för att ersätta dem med en anpassad högnivåhändelse. Till exempel markerar WPF-ButtonBase den bubblande indatahändelsen MouseLeftButtonDown som hanterad i dess OnMouseLeftButtonDown-metod och utlöser händelsen Click. Den MouseLeftButtonDown
händelsen och dess händelsedata fortsätter fortfarande längs elementträdsvägen, men eftersom händelsen markeras som Handled i händelsedata anropas endast hanterare som är konfigurerade för att svara på hanterade händelser.
Om du vill att andra element närmare roten av ditt program ska hantera en dirigerad händelse som är markerad som hanterad kan du antingen:
Koppla hanterare genom att anropa metoden UIElement.AddHandler(RoutedEvent, Delegate, Boolean) och ange parametern
handledEventsToo
tilltrue
. Den här metoden kräver att du kopplar händelsehanteraren i code-behind när du har hämtat en objektreferens till elementet som den ska kopplas till.Om händelsen som markerats som hanterad är en bubblande händelse bifogar du hanterare för motsvarande förhandsgranskningshändelse om den är tillgänglig. Om en kontroll till exempel undertrycker händelsen MouseLeftButtonDown kan du koppla en hanterare för den PreviewMouseLeftButtonDown händelsen i stället. Den här metoden fungerar bara för indatahändelser för baselement som implementerar både tunnel och bubblande routningsstrategier och som delar händelsedata.
I följande exempel implementeras en rudimentär anpassad kontroll med namnet componentWrapper
som innehåller en TextBox. Kontrollen läggs till i en StackPanel med namnet 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>
Den componentWrapper
kontrollen lyssnar efter den KeyDown bubblande händelsen som genereras av dess TextBox
komponent när en tangenttryckning inträffar. Vid den händelsen, kontrollen componentWrapper
:
Markerar den
KeyDown
bubblande dirigerade händelsen så som den hanteras för att förhindra den. Därför utlöses endast denouterStackPanel
-hanterare som har konfigurerats i den bakomliggande koden för att reagera på hanteradeKeyDown
händelser.outerStackPanel
-hanteraren som är kopplad till XAML förKeyDown
händelser anropas inte.Genererar en anpassad bubblande dirigerad händelse med namnet
CustomKey
, vilket utlöserouterStackPanel
-hanteraren för händelsenCustomKey
.
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
Exemplet visar två arbetsmetoder för att få den undertryckta KeyDown
routed händelsen att anropa en händelsehanterare kopplad till outerStackPanel
:
Koppla en PreviewKeyDown händelsehanterare till
outerStackPanel
. Eftersom en förhandsgranskningsdirigerad händelse föregår motsvarande bubblande dirigerade händelse körsPreviewKeyDown
-hanteraren i exemplet föreKeyDown
-hanteraren som suppressar både förhandsgransknings- och bubblande händelser via deras delade händelsedata.Koppla en
KeyDown
händelsehanterare tillouterStackPanel
med hjälp av metoden UIElement.AddHandler(RoutedEvent, Delegate, Boolean) i code-behind, med parameternhandledEventsToo
inställd påtrue
.
Notera
Att markera förhandsgransknings- eller icke-förhandsversionsmotsvarigheter för indatahändelser som hanteras är båda strategier för att förhindra händelser som genereras av komponenterna i en kontroll. Vilken metod du använder beror på dina programkrav.
Se även
.NET Desktop feedback