預覽事件 (WPF .NET)
預覽事件,也稱為通道事件,是路由事件,從應用程式根項目向下周遊專案樹狀結構到引發事件的元素。 引發事件的 元素會回報為 Source 事件資料中的 。 並非所有事件案例都支援或需要預覽事件。 本文說明預覽事件存在的位置,以及應用程式或元件如何與其互動。 如需如何建立預覽事件的資訊,請參閱 如何建立自訂路由事件 。
重要
.NET 7 和 .NET 6 的桌面指南檔正在建置中。
必要條件
本文假設您已瞭解路由事件,而且您已閱讀 路由事件概觀 。 若要遵循本文中的範例,如果您熟悉可延伸的應用程式標記語言(XAML),並知道如何撰寫 Windows Presentation Foundation (WPF) 應用程式,它很有説明。
標示為已處理的預覽事件
將預覽事件標示為事件資料中所處理時,請小心。 將預覽事件標示為在引發之專案以外的專案上處理,可以防止引發預覽事件的專案處理事件。 有時會刻意將預覽事件標示為已處理。 例如,複合控制項可能會隱藏個別元件所引發的事件,並以完整控制項所引發的事件取代它們。 控制項的自訂事件可以根據元件狀態關聯性提供自訂事件資料和觸發程式。
針對 輸入事件 ,事件資料會由每個事件的預覽和非預覽(升階)對等專案共用。 如果您使用預覽事件類別處理常式將輸入事件標示為已處理,通常不會叫用反升輸入事件的類別處理常式。 或者,如果您使用預覽事件實例處理常式將事件標示為已處理,則通常不會叫用升階輸入事件的實例處理常式。 雖然您可以將類別和實例處理常式設定為叫用,即使事件標示為已處理,該處理常式組態並不常見。 如需類別處理及其與預覽事件關聯的詳細資訊,請參閱 將路由事件標示為已處理和類別處理 。
注意
並非所有預覽事件都是 通道 事件。 例如, PreviewMouseLeftButtonDown 輸入事件會透過元素樹狀結構追蹤向下路由,但是由 路由中的每個事件引發和重新引發 UIElement 的直接 路由事件。
依控制項處理事件隱藏
某些複合控制項會隱藏元件層級的輸入事件,以自訂的高階事件取代它們。 例如,WPF ButtonBase 會將反升輸入事件標示 MouseLeftButtonDown 為在其 OnMouseLeftButtonDown 方法中處理,並引發 Click 事件。 事件 MouseLeftButtonDown
及其事件資料仍會沿著元素樹狀目錄路由繼續,但由於事件在事件資料中標示為 Handled ,因此只會叫用設定為回應已處理事件的處理常式。
如果您想要讓應用程式根目錄的其他元素處理標示為已處理的路由事件,您可以:
呼叫 方法並將 參數
handledEventsToo
設定為true
, UIElement.AddHandler(RoutedEvent, Delegate, Boolean) 以附加處理常式。 此方法需要在程式碼後置中附加事件處理常式,才能取得要附加至之專案的物件參考。如果標示為已處理的事件是反升事件,則如果有的話,請附加對等預覽事件的處理常式。 例如,如果控制項隱藏 MouseLeftButtonDown 事件,您可以改為附加事件的處理常式 PreviewMouseLeftButtonDown 。 此方法僅適用于實作通道 和升階 路由策略並 共用事件資料的基底元素輸入事件 。
下列範例會實作名為 的粗細自訂控制項,其中包含 componentWrapper
TextBox 。 控制項會新增至 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>
每當發生擊鍵時, KeyDown 控制項 componentWrapper
會接聽其 TextBox
元件引發的反升事件。 在該發生時, componentWrapper
控制項:
KeyDown
將反升路由事件標示為已處理以隱藏事件。 因此,只會觸發程式outerStackPanel
代碼後置中設定以回應 已處理KeyDown
事件的處理常式。outerStackPanel
不會叫用 XAML 中為KeyDown
事件附加的處理常式。引發名為
CustomKey
的自訂反升路由事件,這會觸發outerStackPanel
事件的處理常式CustomKey
。
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
此範例示範取得隱藏 KeyDown
路由事件的兩個因應措施,以叫用附加至 的 outerStackPanel
事件處理常式:
將 PreviewKeyDown 事件處理常式附加至
outerStackPanel
。 由於預覽輸入路由事件在對等的反升路由事件之前,範例中的處理常式會在處理常式前面KeyDown
執行,PreviewKeyDown
透過其共用事件資料隱藏預覽和反升事件。KeyDown
使用 UIElement.AddHandler(RoutedEvent, Delegate, Boolean) 程式碼後置中的 方法,將handledEventsToo
事件處理常式附加至outerStackPanel
,並將 參數設定為true
。
注意
標記所處理之輸入事件的預覽或非預覽對等專案,都是隱藏控制群組件所引發事件的策略。 您所使用的方法取決於您的應用程式需求。
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應