Události ve verzi Preview (WPF .NET)

Události verze Preview, označované také jako události tunelování, jsou směrované události, které procházejí stromem elementů z kořenového prvku aplikace do elementu, který vyvolal událost. Prvek, který vyvolá událost, je hlášen jako Source v datech události. Ne všechny scénáře událostí podporují nebo vyžadují události ve verzi Preview. Tento článek popisuje, kde existují události ve verzi Preview a jak s nimi můžou aplikace nebo komponenty pracovat. Informace o tom, jak vytvořit událost ve verzi Preview, najdete v tématu Vytvoření vlastní směrované události.

Důležité

Dokumentace k desktopové příručce pro .NET 7 a .NET 6 se právě připravuje.

Předpoklady

V článku se předpokládá základní znalost směrovaných událostí a že jste si přečetli přehled směrovaných událostí. Pokud chcete postupovat podle příkladů v tomto článku, pomůže vám to, pokud znáte jazyk XAML (Extensible Application Markup Language) a víte, jak psát aplikace WINDOWS Presentation Foundation (WPF).

Náhled událostí označených jako zpracovávané

Při označování událostí náhledu jako zpracovaných v datech událostí buďte opatrní. Označení události náhledu jako zpracovávané u jiného prvku, než je prvek, který ho vyvolal, může zabránit prvku, který ho vyvolal v zpracování události. Někdy je označení událostí náhledu úmyslné. Složený ovládací prvek může například potlačit události vyvolané jednotlivými komponentami a nahradit je událostmi vyvolanými úplným ovládacím prvku. Vlastní události ovládacího prvku můžou poskytovat přizpůsobená data událostí a triggery na základě relací stavu součástí.

U vstupních událostí jsou data událostí sdílena ekvivalenty jednotlivých událostí ve verzi Preview i bez náhledu (bublání). Pokud použijete obslužnou rutinu třídy událostí ve verzi Preview k označení vstupní události jako obslužné rutiny, obslužné rutiny třídy pro vstupní událost bublání obvykle nebudou vyvolány. Nebo pokud použijete obslužnou rutinu instance události ve verzi Preview k označení události jako obslužné rutiny, obslužné rutiny instance pro vstupní událost bublání obvykle nebudou vyvolány. I když můžete nakonfigurovat obslužné rutiny třídy a instance tak, aby byly vyvolány i v případě, že je událost označena jako zpracována, tato konfigurace obslužné rutiny není běžná. Další informace o zpracování tříd a o tom, jak souvisí s událostmi verze Preview, najdete v tématu Označení směrovaných událostí jako zpracovávaných a zpracování tříd.

Poznámka:

Ne všechny události ve verzi Preview jsou události tunelování . Vstupní událost se například PreviewMouseLeftButtonDown řídí dolů přes strom prvků, ale je to přímá směrovaná událost, která je vyvolána a reraizována každou UIElement trasou v trase.

Práce s potlačením událostí ovládacími prvky

Některé složené ovládací prvky potlačí vstupní události na úrovni komponenty, aby je nahradily přizpůsobenou událostí vysoké úrovně. WpF ButtonBase například označí MouseLeftButtonDown vstupní událost bublajícího vstupu, jak je zpracována ve své OnMouseLeftButtonDown metodě, a vyvolá Click událost. Událost MouseLeftButtonDown a její data události stále pokračují podél trasy stromu prvků, ale protože událost je označena jako Handled v datech událostí, jsou vyvolány pouze obslužné rutiny nakonfigurované tak, aby reagovaly na zpracovávané události.

Pokud chcete, aby ostatní prvky směrem ke kořenovému adresáři vaší aplikace zpracovávaly směrovanou událost označenou jako popisovanou, můžete:

  • Připojte obslužné rutiny voláním UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metody a nastavením parametru handledEventsToo na true. Tento přístup vyžaduje připojení obslužné rutiny události v kódu za sebou, po získání odkazu objektu na prvek, ke kterému se připojí.

  • Pokud je událost označená jako obslužná událost bublající událost, připojte obslužné rutiny ekvivalentní události náhledu, pokud jsou k dispozici. Pokud například ovládací prvek potlačí MouseLeftButtonDown událost, můžete místo toho připojit obslužnou rutinu PreviewMouseLeftButtonDown události. Tento přístup funguje pouze u vstupních událostí základního elementu, které implementují strategie směrování tunelování i bublinové směrování a sdílejí data událostí.

Následující příklad implementuje základní vlastní ovládací prvek s názvem componentWrapper , který obsahuje TextBox. Ovládací prvek se přidá do pojmenovaného StackPanelouterStackPanel.

<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>

Ovládací componentWrapper prvek naslouchá KeyDown události bublání vyvolané jeho TextBox komponentou při každém stisknutí klávesy. V takovém případě ovládací prvek componentWrapper :

  1. Označí událost směrovanou bublinou KeyDown tak, aby ji potlačovala. V důsledku toho se aktivuje pouze outerStackPanel obslužná rutina nakonfigurovaná v kódu za účelem reakce na zpracovávanéKeyDown události. Obslužná rutina outerStackPanel připojená v XAML pro KeyDown události není vyvolána.

  2. Vyvolá vlastní událost směrovanou bublinou s názvem CustomKey, která aktivuje obslužnou rutinu outerStackPanelCustomKey události.

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

Příklad ukazuje dvě alternativní řešení pro získání potlačené směrované KeyDown události vyvolat obslužnou rutinu události připojenou k outerStackPanel:

  • Připojte obslužnou rutinu PreviewKeyDown události k sadě outerStackPanel. Vzhledem k tomu, PreviewKeyDown že vstupní událost směrovaná ve verzi Preview předchází ekvivalentní směrované události, obslužná rutina v příkladu se spustí před KeyDown obslužnou rutinou, která potlačuje události náhledu i bublání prostřednictvím jejich sdílených dat událostí.

  • Připojte obslužnou rutinu KeyDownouterStackPanel události pomocí UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metody v kódu na pozadí s parametrem nastaveným handledEventsToo na true.

Poznámka:

Označení náhledu nebo ekvivalentů vstupních událostí, které nejsou ve verzi Preview, jsou obě strategie potlačení událostí vyvolaných komponentami ovládacího prvku. Přístup, který používáte, závisí na požadavcích vaší aplikace.

Viz také