Eventi di anteprima (WPF .NET)
Gli eventi di anteprima, noti anche come eventi di tunneling, sono eventi indirizzati che attraversano verso il basso l'albero degli elementi dall'elemento radice dell'applicazione all'elemento che ha generato l'evento. L'elemento che genera un evento viene segnalato come nell'oggetto nei dati dell'evento Source . Non tutti gli scenari di eventi supportano o richiedono eventi di anteprima. Questo articolo descrive dove esistono eventi di anteprima e come le applicazioni o i componenti possono interagire con essi. Per informazioni su come creare un evento di anteprima, vedere Come creare un evento indirizzato personalizzato.
Prerequisiti
L'articolo presuppone una conoscenza di base degli eventi indirizzati e di aver letto panoramica degli eventi indirizzati. Per seguire gli esempi in questo articolo, è utile se si ha familiarità con Extensible Application Markup Language (XAML) e si sa come scrivere applicazioni Windows Presentation Foundation (WPF).
Eventi di anteprima contrassegnati come gestiti
Prestare attenzione quando si contrassegnano gli eventi di anteprima come gestiti nei dati dell'evento. Contrassegnando un evento di anteprima come gestito su un elemento diverso dall'elemento che lo ha generato può impedire all'elemento che lo ha generato di gestire l'evento. A volte contrassegnare gli eventi di anteprima come gestiti è intenzionale. Ad esempio, un controllo composito potrebbe eliminare gli eventi generati dai singoli componenti e sostituirli con gli eventi generati dal controllo completo. Gli eventi personalizzati per un controllo possono fornire dati e trigger di eventi personalizzati in base alle relazioni di stato del componente.
Per gli eventi di input, i dati degli eventi vengono condivisi sia dall'anteprima che dagli equivalenti non di anteprima (bubbling) di ogni evento. Se si usa un gestore della classe di evento di anteprima per contrassegnare un evento di input come gestito, i gestori di classe per l'evento di input di bubbling in genere non verranno richiamati. In alternativa, se si usa un gestore dell'istanza dell'evento di anteprima per contrassegnare un evento come gestito, i gestori di istanza per l'evento di input di bubbling in genere non verranno richiamati. Sebbene sia possibile configurare gestori di classi e istanze da richiamare anche se un evento è contrassegnato come gestito, tale configurazione del gestore non è comune. Per altre informazioni sulla gestione delle classi e sulla relativa correlazione con gli eventi di anteprima, vedere Contrassegnare gli eventi indirizzati come gestiti e la gestione delle classi.
Nota
Non tutti gli eventi di anteprima sono eventi di tunneling . Ad esempio, l'evento PreviewMouseLeftButtonDown di input segue una route verso il basso attraverso l'albero degli elementi, ma è un evento indirizzato diretto che viene generato e generato da ognuno UIElement nella route.
Uso dell'eliminazione degli eventi da parte dei controlli
Alcuni controlli compositi eliminano gli eventi di input a livello di componente per sostituirli con un evento di alto livello personalizzato. Ad esempio, WPF ButtonBase contrassegna l'evento MouseLeftButtonDown di input di bubbling come gestito nel relativo OnMouseLeftButtonDown metodo e genera l'evento Click . L'evento MouseLeftButtonDown
e i relativi dati dell'evento continuano ancora lungo la route dell'albero degli elementi, ma poiché l'evento è contrassegnato come Handled nei dati dell'evento, vengono richiamati solo i gestori configurati per rispondere agli eventi gestiti.
Se si desidera che altri elementi verso la radice dell'applicazione gestisca un evento indirizzato contrassegnato come gestito, è possibile:
Collegare i gestori chiamando il UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metodo e impostando il parametro
handledEventsToo
sutrue
. Questo approccio richiede l'associazione del gestore eventi nel code-behind, dopo aver ottenuto un riferimento a un oggetto all'elemento a cui verrà associato.Se l'evento contrassegnato come gestito è un evento di bubbling, collegare gestori per l'evento di anteprima equivalente, se disponibile. Ad esempio, se un controllo elimina l'evento MouseLeftButtonDown , è possibile associare un gestore per l'evento PreviewMouseLeftButtonDown . Questo approccio funziona solo per gli eventi di input degli elementi di base che implementano strategie di routing di tunneling e bubbling e condividono i dati degli eventi.
Nell'esempio seguente viene implementato un controllo personalizzato rudimentale denominato componentWrapper
che contiene un oggetto TextBox. Il controllo viene aggiunto a un StackPanel oggetto denominato 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>
Il componentWrapper
controllo è in ascolto dell'evento KeyDown di bubbling generato dal relativo TextBox
componente ogni volta che si verifica una sequenza di tasti. In tale occorrenza, il componentWrapper
controllo :
Contrassegna l'evento
KeyDown
indirizzato di bubbling come gestito per eliminarlo. Di conseguenza, viene attivato solo ilouterStackPanel
gestore configurato nel code-behind per rispondere agli eventi gestitiKeyDown
. IlouterStackPanel
gestore collegato in XAML perKeyDown
gli eventi non viene richiamato.Genera un evento indirizzato di bubbling personalizzato denominato
CustomKey
, che attiva ilouterStackPanel
gestore per l'eventoCustomKey
.
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
Nell'esempio vengono illustrate due soluzioni alternative per ottenere l'evento indirizzato eliminato KeyDown
per richiamare un gestore eventi associato a outerStackPanel
:
Collegare un PreviewKeyDown gestore eventi all'oggetto
outerStackPanel
. Poiché un evento indirizzato di input di anteprima precede l'evento indirizzato bubbling equivalente, ilPreviewKeyDown
gestore nell'esempio viene eseguito prima delKeyDown
gestore che elimina gli eventi di anteprima e di bubbling tramite i dati degli eventi condivisi.Collegare un
KeyDown
gestore eventi aouterStackPanel
usando il UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metodo nel code-behind, con ilhandledEventsToo
parametro impostato sutrue
.
Nota
Contrassegnare gli equivalenti di anteprima o non di anteprima degli eventi di input gestiti sono entrambe le strategie per eliminare gli eventi generati dai componenti di un controllo. L'approccio usato dipende dai requisiti dell'applicazione.
Vedi anche
.NET Desktop feedback