Bagikan melalui


Pratinjau peristiwa

Peristiwa pratinjau, juga dikenal sebagai peristiwa penerowongan, adalah peristiwa yang dirutekan yang bergerak menuruni pohon elemen dari elemen akar aplikasi ke elemen yang memicu peristiwa. Elemen yang menaikkan peristiwa dilaporkan sebagai Source dalam data peristiwa. Tidak semua skenario acara mendukung atau memerlukan pratinjau acara. Artikel ini menjelaskan di mana peristiwa pratinjau ada dan bagaimana aplikasi atau komponen dapat berinteraksi dengannya. Untuk informasi tentang cara membuat event pratinjau, lihat Cara membuat event routed kustom.

Prasyarat

Artikel ini mengasumsikan pengetahuan dasar tentang event yang dirutekan, dan bahwa Anda telah membaca Gambaran Umum Event yang Dirutekan. Untuk mengikuti contoh dalam artikel ini, ini membantu jika Anda terbiasa dengan Extensible Application Markup Language (XAML) dan tahu cara menulis aplikasi Windows Presentation Foundation (WPF).

Pratinjau event yang ditandai sebagai sudah ditangani

Berhati-hatilah saat menandai peristiwa pratinjau sebagai ditangani dalam data peristiwa. Menandai peristiwa pratinjau sebagai sudah ditangani pada elemen lain selain elemen yang memicunya dapat mencegah elemen yang memicu tersebut dari menangani peristiwa tersebut. Terkadang menandai peristiwa pratinjau sebagai telah ditangani adalah tindakan yang disengaja. Misalnya, kontrol komposit mungkin menekan event yang dipicu oleh komponen individual dan menggantinya dengan event yang dipicu oleh kontrol keseluruhan. Peristiwa kustom untuk kontrol dapat menyediakan data peristiwa yang disesuaikan dan pemicu berdasarkan hubungan status komponen.

Untuk kejadian input, data kejadian dibagikan oleh versi pratinjau dan non-pratinjau (gelembung) yang setara untuk setiap kejadian. Jika Anda menggunakan penanganan kelas peristiwa pratinjau untuk menandai peristiwa input sebagai ditangani, penanganan kelas untuk peristiwa input gelembung biasanya tidak akan dipanggil. Atau, jika Anda menggunakan penanganan instans peristiwa pratinjau untuk menandai peristiwa sebagai ditangani, penanganan instans untuk peristiwa input gelembung biasanya tidak akan dipanggil. Meskipun Anda dapat mengonfigurasi penangan kelas dan instans untuk dipanggil bahkan jika peristiwa ditandai sebagai ditangani, konfigurasi handler tersebut tidak umum dilakukan. Untuk informasi selengkapnya tentang penanganan kelas dan hubungannya dengan peristiwa pratinjau, lihat Menandai peristiwa yang dirutekan sebagai sudah ditangani dan penanganan kelas.

Nota

Tidak semua peristiwa pratinjau adalah peristiwa penerowongan . Misalnya, PreviewMouseLeftButtonDown peristiwa input mengikuti rute ke bawah melalui pohon elemen, tetapi merupakan peristiwa rute langsung yang dinaikkan dan disalurkan kembali oleh masing-masing UIElement dalam rute.

Bekerja di sekitar penekanan peristiwa berdasarkan kontrol

Beberapa kontrol komposit menekan peristiwa input di tingkat komponen untuk menggantinya dengan peristiwa tingkat tinggi yang disesuaikan. Misalnya, WPF ButtonBase menandai peristiwa input bubbling sebagai ditangani dalam metodenya OnMouseLeftButtonDown dan memicu peristiwa Click. Peristiwa MouseLeftButtonDown dan data peristiwanya masih terus bergerak melalui jalur pohon elemen, tetapi karena peristiwa tersebut ditandai sebagai Handled dalam data peristiwa, hanya handler yang dikonfigurasi untuk merespons peristiwa yang sudah ditangani yang dipanggil.

Jika Anda ingin elemen lain menuju akar aplikasi Anda menangani peristiwa yang dirutekan yang sudah ditandai sebagai ditangani, Anda dapat:

  • Pasang handler dengan memanggil metode UIElement.AddHandler(RoutedEvent, Delegate, Boolean) dan mengatur parameter handledEventsToo ke true. Pendekatan ini mengharuskan melampirkan penanganan aktivitas di code-behind, setelah mendapatkan referensi objek ke elemen yang akan dilampirkan.

  • Jika peristiwa yang ditandai sebagai ditangani adalah peristiwa gelembung, lampirkan handler untuk peristiwa pratinjau yang setara jika tersedia. Misalnya, jika kontrol menekan peristiwa MouseLeftButtonDown, Anda dapat melampirkan handler untuk peristiwa PreviewMouseLeftButtonDown sebagai gantinya. Pendekatan ini hanya berfungsi untuk peristiwa input elemen dasar yang mengimplementasikan strategi perutean penerowongan dan gelembung, serta membagikan data peristiwa.

Contoh berikut mengimplementasikan kontrol kustom dasar bernama componentWrapper yang berisi TextBox. Kontrol ditambahkan ke StackPanel yang bernama 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>

Kontrol componentWrapper mendengarkan kejadian gelembung KeyDown yang dipicu oleh komponen TextBox setiap kali penekanan tombol terjadi. Pada kejadian tersebutcomponentWrapper, kontrol:

  1. Menandai peristiwa routing bubbling KeyDown sebagai telah ditangani untuk menghambatnya. Akibatnya, hanya outerStackPanel handler yang dikonfigurasi di code-behind untuk merespons peristiwa yang ditanganiKeyDown yang dipicu. Handler outerStackPanel yang dilampirkan di XAML untuk KeyDown event tidak dipanggil.

  2. Mengangkat peristiwa berjalur gelembung kustom bernama CustomKey, yang memicu handler outerStackPanel untuk peristiwa 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

Contoh menunjukkan dua solusi untuk mendapatkan peristiwa rute yang ditekan KeyDown untuk memanggil penanganan aktivitas yang dilampirkan ke outerStackPanel:

  • Lampirkan PreviewKeyDown penanganan aktivitas ke outerStackPanel. Karena peristiwa input pratinjau yang dirutekan mendahului peristiwa gelembung yang dirutekan yang setara, PreviewKeyDown handler dalam contoh berjalan di depan KeyDown handler yang menangani pratinjau dan peristiwa gelembung melalui data peristiwa bersama mereka.

  • Pasang penangan acara ke outerStackPanel dengan menggunakan metode UIElement.AddHandler(RoutedEvent, Delegate, Boolean) di code-behind, dengan parameter handledEventsToo yang diatur ke true.

Nota

Menandai pratinjau atau non-pratinjau yang setara dengan peristiwa input seperti yang ditangani adalah strategi untuk menekan peristiwa yang dimunculkan oleh komponen kontrol. Pendekatan yang Anda gunakan tergantung pada persyaratan aplikasi Anda.

Lihat juga