Bagikan melalui


Pratinjau peristiwa (WPF .NET)

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

Penting

Dokumentasi Panduan Desktop untuk .NET 7 dan .NET 6 sedang dibangun.

Prasyarat

Artikel ini mengasumsikan pengetahuan dasar tentang peristiwa yang dirutekan, dan anda telah membaca ringkasan peristiwa 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 peristiwa yang ditandai sebagai ditangani

Berhati-hatilah saat menandai peristiwa pratinjau sebagai ditangani dalam data peristiwa. Menandai peristiwa pratinjau seperti yang ditangani pada elemen selain elemen yang menaikkannya dapat mencegah elemen yang menaikkannya menangani peristiwa. Terkadang menandai peristiwa pratinjau seperti yang ditangani disengaja. Misalnya, kontrol komposit mungkin menekan peristiwa yang diangkat oleh komponen individual dan menggantinya dengan peristiwa yang dinaikkan oleh kontrol lengkap. Peristiwa kustom untuk kontrol dapat menyediakan data peristiwa yang disesuaikan dan pemicu berdasarkan hubungan status komponen.

Untuk peristiwa input, data peristiwa dibagikan oleh pratinjau dan non-pratinjau (gelembung) yang setara dengan setiap peristiwa. 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. Untuk informasi selengkapnya tentang penanganan kelas dan hubungannya dengan peristiwa pratinjau, lihat Menandai peristiwa yang dirutekan sebagai penanganan dan penanganan kelas.

Catatan

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 MouseLeftButtonDown peristiwa input gelembung seperti yang ditangani dalam metodenya OnMouseLeftButtonDown dan menaikkan Click peristiwa. Peristiwa MouseLeftButtonDown dan data peristiwanya masih berlanjut di sepanjang rute pohon elemen, tetapi karena peristiwa ditandai sebagai Handled dalam data peristiwa, hanya penangan yang dikonfigurasi untuk merespons peristiwa yang ditangani yang dipanggil.

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

  • Lampirkan handler dengan memanggil UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metode 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 MouseLeftButtonDown peristiwa, Anda dapat melampirkan handler untuk peristiwa sebagai gantinya PreviewMouseLeftButtonDown . Pendekatan ini hanya berfungsi untuk peristiwa input elemen dasar yang mengimplementasikan strategi perutean penerowongan dan bubbling dan berbagi data peristiwa.

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

Kontrol componentWrapper mendengarkan peristiwa gelembung yang KeyDown diangkat oleh komponennya TextBox setiap kali penekanan tombol terjadi. Pada kejadian tersebut componentWrapper , kontrol:

  1. KeyDown Menandai peristiwa bergelombang yang dirutekan sebagai ditangani untuk menekannya. Akibatnya, hanya outerStackPanel handler yang dikonfigurasi di code-behind untuk merespons peristiwa yang ditanganiKeyDown yang dipicu. Handler outerStackPanel yang dilampirkan di XAML untuk KeyDown peristiwa tidak dipanggil.

  2. Menaikkan peristiwa rute gelembung kustom bernama CustomKey, yang memicu outerStackPanel handler untuk peristiwa tersebut 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 yang dirutekan input pratinjau mendahului peristiwa yang dirutekan gelembung yang setara, PreviewKeyDown handler dalam contoh berjalan di depan KeyDown handler yang menekan pratinjau dan peristiwa gelembung melalui data peristiwa bersama mereka.

  • KeyDown Lampirkan penanganan aktivitas ke outerStackPanel dengan menggunakan UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metode di code-behind, dengan parameter diatur handledEventsToo ke true.

Catatan

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.

Baca juga