Bagikan melalui


Menandai peristiwa yang dirutekan sebagai ditangani, dan penanganan kelas (WPF .NET)

Meskipun tidak ada aturan absolut kapan harus menandai peristiwa yang dirutekan sebagai ditangani, pertimbangkan untuk menandai peristiwa sebagai ditangani jika kode Anda merespons peristiwa dengan cara yang signifikan. Peristiwa rute yang ditandai sebagai ditangani akan berlanjut di sepanjang rutenya, tetapi hanya handler yang dikonfigurasi untuk merespons peristiwa yang ditangani yang dipanggil. Pada dasarnya, menandai peristiwa yang dirutekan sebagai ditangani membatasi visibilitasnya kepada pendengar di sepanjang rute peristiwa.

Penanganan aktivitas yang dirutekan dapat berupa handler instans atau handler kelas. Penangan instans menangani peristiwa yang dirutekan pada objek atau elemen XAML. Handler kelas menangani peristiwa yang dirutekan pada tingkat kelas, dan dipanggil sebelum handler instans apa pun merespons peristiwa yang sama pada instans kelas apa pun. Saat peristiwa yang dirutekan ditandai sebagai ditangani, peristiwa tersebut sering ditandai seperti itu dalam penangan kelas. Artikel ini membahas manfaat dan potensi jebakan menandai peristiwa yang dirutekan sebagai ditangani, berbagai jenis peristiwa yang dirutekan dan penanganan peristiwa yang dirutekan, dan penindasan peristiwa dalam kontrol komposit.

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

Kapan menandai peristiwa yang dirutekan sebagai ditangani

Biasanya, hanya satu handler yang harus memberikan respons yang signifikan untuk setiap peristiwa yang dirutekan. Hindari menggunakan sistem peristiwa yang dirutekan untuk memberikan respons yang signifikan di beberapa handler. Definisi dari apa yang merupakan respons yang signifikan bersifat subjektif dan tergantung pada aplikasi Anda. Sebagai panduan umum:

  • Respons signifikan termasuk mengatur fokus, memodifikasi status publik, mengatur properti yang memengaruhi representasi visual, meningkatkan peristiwa baru, dan sepenuhnya menangani peristiwa.
  • Respons yang tidak signifikan termasuk memodifikasi status privat tanpa dampak visual atau terprogram, pengelogan peristiwa, dan memeriksa data peristiwa tanpa menanggapi peristiwa tersebut.

Beberapa kontrol WPF menekan peristiwa tingkat komponen yang tidak memerlukan penanganan lebih lanjut dengan menandainya sebagai ditangani. Jika Anda ingin menangani peristiwa yang ditandai sebagai ditangani oleh kontrol, lihat Mengatasi penekanan peristiwa menurut kontrol.

Untuk menandai peristiwa sebagai ditangani, atur Handled nilai properti dalam data peristiwanya ke true. Meskipun dimungkinkan untuk mengembalikan nilai tersebut ke false, kebutuhan untuk melakukannya harus jarang terjadi.

Pratinjau dan pasangan peristiwa yang dirutekan

Pratinjau dan pasangan peristiwa yang dirutekan bersifat khusus untuk peristiwa input. Beberapa peristiwa input menerapkan penerowongan dan menggelegak pasangan peristiwa yang dirutekan, seperti PreviewKeyDown dan KeyDown. Preview Awalan menandakan bahwa peristiwa gelembung dimulai setelah peristiwa pratinjau selesai. Setiap pratinjau dan pasangan peristiwa gelembung berbagi instans data peristiwa yang sama.

Penanganan aktivitas yang dirutekan dipanggil dalam urutan yang sesuai dengan strategi perutean peristiwa:

  1. Peristiwa pratinjau melakukan perjalanan dari elemen akar aplikasi ke elemen yang menaikkan peristiwa yang dirutekan. Pratinjau penanganan aktivitas yang dilampirkan ke elemen akar aplikasi dipanggil terlebih dahulu, diikuti oleh handler yang dilampirkan ke elemen berlapis berturut-turut.
  2. Setelah peristiwa pratinjau selesai, peristiwa gelembung yang dipasangkan melakukan perjalanan dari elemen yang menaikkan peristiwa yang dirutekan ke elemen akar aplikasi. Penanganan aktivitas gelembung yang dilampirkan ke elemen yang sama yang menaikkan peristiwa yang dirutekan dipanggil terlebih dahulu, diikuti oleh handler yang dilampirkan ke elemen induk berturut-turut.

Pratinjau berpasangan dan peristiwa gelembung adalah bagian dari implementasi internal beberapa kelas WPF yang mendeklarasikan dan menaikkan peristiwa rute mereka sendiri. Tanpa implementasi internal tingkat kelas tersebut, pratinjau dan peristiwa yang dirutekan sepenuhnya terpisah dan tidak akan berbagi data peristiwa—terlepas dari penamaan peristiwa. Untuk informasi tentang cara menerapkan peristiwa yang dirutekan input gelembung atau penerowongan di kelas kustom, lihat Membuat peristiwa rute kustom.

Karena setiap pratinjau dan pasangan peristiwa gelembung berbagi instans data peristiwa yang sama, jika peristiwa yang dirutekan pratinjau ditandai sebagai ditangani, peristiwa gelembung yang dipasangkan juga akan ditangani. Jika peristiwa bergelombang yang dirutekan ditandai sebagai ditangani, peristiwa pratinjau yang dipasangkan tidak akan memengaruhi peristiwa pratinjau yang dipasangkan karena peristiwa pratinjau selesai. Berhati-hatilah saat menandai pratinjau dan pasangan peristiwa input menggelegak seperti yang ditangani. Peristiwa input pratinjau yang ditangani tidak akan memanggil penanganan aktivitas yang biasanya terdaftar untuk sisa rute penerowongan, dan peristiwa gelembung yang dipasangkan tidak akan dinaikkan. Peristiwa input gelembung yang ditangani tidak akan memanggil penanganan aktivitas yang biasanya terdaftar untuk sisa rute yang menggelegak.

Penanganan aktivitas instans dan kelas yang dirutekan

Penanganan aktivitas yang dirutekan dapat berupa handler instans atau handler kelas . Handler kelas untuk kelas tertentu dipanggil sebelum handler instans apa pun merespons peristiwa yang sama pada instans kelas tersebut. Karena perilaku ini, ketika peristiwa yang dirutekan ditandai sebagai ditangani, peristiwa tersebut sering ditandai seperti itu dalam penangan kelas. Ada dua jenis penangan kelas:

  • Penanganan aktivitas kelas statis, yang didaftarkan dengan memanggil RegisterClassHandler metode dalam konstruktor kelas statis.
  • Ambil alih penanganan aktivitas kelas, yang didaftarkan dengan mengambil alih metode peristiwa virtual kelas dasar. Metode peristiwa virtual kelas dasar terutama ada untuk peristiwa input, dan memiliki nama yang dimulai dengan Nama peristiwa On<dan nama>> peristiwa OnPreview.<

Penanganan aktivitas instans

Anda dapat melampirkan handler instans ke objek atau elemen XAML dengan langsung memanggil AddHandler metode . Peristiwa yang dirutekan WPF menerapkan pembungkus AddHandler peristiwa runtime bahasa umum (CLR) yang menggunakan metode untuk melampirkan penanganan aktivitas. Karena sintaks atribut XAML untuk melampirkan penanganan aktivitas menghasilkan panggilan ke pembungkus AddHandler peristiwa CLR, bahkan melampirkan handler di XAML menyelesaikan panggilan. Untuk peristiwa yang ditangani:

  • Handler yang dilampirkan menggunakan sintaks atribut XAML atau tanda tangan AddHandler umum tidak dipanggil.
  • Handler yang dilampirkan menggunakan AddHandler(RoutedEvent, Delegate, Boolean) kelebihan beban dengan parameter yang handledEventsToo diatur ke true dipanggil. Kelebihan beban ini tersedia untuk kasus yang jarang terjadi ketika perlu merespons peristiwa yang ditangani. Misalnya, beberapa elemen di pohon elemen telah menandai peristiwa sebagai ditangani, tetapi elemen lain lebih jauh di sepanjang rute peristiwa perlu merespons peristiwa yang ditangani.

Sampel XAML berikut menambahkan kontrol kustom bernama componentWrapper, yang membungkus TextBox bernama componentTextBox, ke bernama StackPanel outerStackPanel. Penanganan aktivitas instans untuk PreviewKeyDown peristiwa dilampirkan ke componentWrapper sintaks atribut XAML menggunakan. Akibatnya, handler instans hanya akan merespons peristiwa penerowongan yang tidak tertangani PreviewKeyDown yang dibesarkan componentTextBoxoleh .

<StackPanel Name="outerStackPanel" VerticalAlignment="Center">
    <custom:ComponentWrapper
        x:Name="componentWrapper"
        TextBox.PreviewKeyDown="HandlerInstanceEventInfo"
        HorizontalAlignment="Center">
        <TextBox Name="componentTextBox" Width="200" />
    </custom:ComponentWrapper>
</StackPanel>

MainWindow Konstruktor melampirkan handler instans untuk KeyDown peristiwa gelembung ke componentWrapper penggunaan UIElement.AddHandler(RoutedEvent, Delegate, Boolean) kelebihan beban, dengan handledEventsToo parameter diatur ke true. Akibatnya, penanganan aktivitas instans akan merespons peristiwa yang tidak tertangani dan ditangani.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Attach an instance handler on componentWrapper that will be invoked by handled KeyDown events.
        componentWrapper.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler.InstanceEventInfo),
            handledEventsToo: true);
    }

    // The handler attached to componentWrapper in XAML.
    public void HandlerInstanceEventInfo(object sender, KeyEventArgs e) => 
        Handler.InstanceEventInfo(sender, e);
}
Partial Public Class MainWindow
    Inherits Window

    Public Sub New()
        InitializeComponent()

        ' Attach an instance handler on componentWrapper that will be invoked by handled KeyDown events.
        componentWrapper.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf InstanceEventInfo),
                                      handledEventsToo:=True)
    End Sub

    ' The handler attached to componentWrapper in XAML.
    Public Sub HandlerInstanceEventInfo(sender As Object, e As KeyEventArgs)
        InstanceEventInfo(sender, e)
    End Sub

End Class

Implementasi ComponentWrapper code-behind ditampilkan di bagian berikutnya.

Penanganan aktivitas kelas statis

Anda dapat melampirkan penanganan aktivitas kelas statis dengan memanggil RegisterClassHandler metode di konstruktor statis kelas. Setiap kelas dalam hierarki kelas dapat mendaftarkan handler kelas statisnya sendiri untuk setiap peristiwa yang dirutekan. Akibatnya, mungkin ada beberapa handler kelas statis yang dipanggil untuk peristiwa yang sama pada node tertentu dalam rute peristiwa. Saat rute peristiwa untuk peristiwa dibangun, semua handler kelas statis untuk setiap node ditambahkan ke rute peristiwa. Urutan pemanggilan handler kelas statis pada node dimulai dengan handler kelas statis yang paling turunan, diikuti oleh handler kelas statis dari setiap kelas dasar berturut-turut.

Penanganan aktivitas kelas statis yang terdaftar menggunakan RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) kelebihan beban dengan parameter yang handledEventsToo diatur untuk true akan merespons peristiwa rute yang tidak tertangani dan ditangani.

Handler kelas statis biasanya didaftarkan untuk merespons peristiwa yang tidak tertangani saja. Dalam hal ini, jika handler kelas turunan pada node menandai peristiwa sebagai handler kelas dasar untuk peristiwa tersebut tidak akan dipanggil. Dalam skenario itu, handler kelas dasar secara efektif digantikan oleh handler kelas turunan. Penangan kelas dasar sering berkontribusi pada desain kontrol di area seperti tampilan visual, logika status, penanganan input, dan penanganan perintah, jadi berhati-hatilah untuk menggantinya. Penangan kelas turunan yang tidak menandai peristiwa sebagai ditangani akhirnya melengkapi penangan kelas dasar alih-alih menggantinya.

Sampel kode berikut menunjukkan hierarki kelas untuk ComponentWrapper kontrol kustom yang direferensikan dalam XAML sebelumnya. Kelas ComponentWrapper ini berasal dari ComponentWrapperBase kelas , yang pada gilirannya StackPanel berasal dari kelas . Metode ini RegisterClassHandler , yang digunakan dalam konstruktor statis kelas ComponentWrapper dan ComponentWrapperBase , mendaftarkan penanganan aktivitas kelas statis untuk setiap kelas tersebut. Sistem peristiwa WPF memanggil ComponentWrapper handler kelas statis di depan ComponentWrapperBase handler kelas statis.

public class ComponentWrapper : ComponentWrapperBase
{
    static ComponentWrapper()
    {
        // Class event handler implemented in the static constructor.
        EventManager.RegisterClassHandler(typeof(ComponentWrapper), KeyDownEvent, 
            new RoutedEventHandler(Handler.ClassEventInfo_Static));
    }

    // Class event handler that overrides a base class virtual method.
    protected override void OnKeyDown(KeyEventArgs e)
    {
        Handler.ClassEventInfo_Override(this, e);

        // Call the base OnKeyDown implementation on ComponentWrapperBase.
        base.OnKeyDown(e);
    }
}

public class ComponentWrapperBase : StackPanel
{
    // Class event handler implemented in the static constructor.
    static ComponentWrapperBase()
    {
        EventManager.RegisterClassHandler(typeof(ComponentWrapperBase), KeyDownEvent, 
            new RoutedEventHandler(Handler.ClassEventInfoBase_Static));
    }

    // Class event handler that overrides a base class virtual method.
    protected override void OnKeyDown(KeyEventArgs e)
    {
        Handler.ClassEventInfoBase_Override(this, e);

        e.Handled = true;
        Debug.WriteLine("The KeyDown routed event is marked as handled.");

        // Call the base OnKeyDown implementation on StackPanel.
        base.OnKeyDown(e);
    }
}
Public Class ComponentWrapper
    Inherits ComponentWrapperBase

    Shared Sub New()
        ' Class event handler implemented in the static constructor.
        EventManager.RegisterClassHandler(GetType(ComponentWrapper), KeyDownEvent,
                                          New RoutedEventHandler(AddressOf ClassEventInfo_Static))
    End Sub

    ' Class event handler that overrides a base class virtual method.
    Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
        ClassEventInfo_Override(Me, e)

        ' Call the base OnKeyDown implementation on ComponentWrapperBase.
        MyBase.OnKeyDown(e)
    End Sub

End Class

Public Class ComponentWrapperBase
    Inherits StackPanel

    Shared Sub New()
        ' Class event handler implemented in the static constructor.
        EventManager.RegisterClassHandler(GetType(ComponentWrapperBase), KeyDownEvent,
                                          New RoutedEventHandler(AddressOf ClassEventInfoBase_Static))
    End Sub

    ' Class event handler that overrides a base class virtual method.
    Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
        ClassEventInfoBase_Override(Me, e)

        e.Handled = True
        Debug.WriteLine("The KeyDown event is marked as handled.")

        ' Call the base OnKeyDown implementation on StackPanel.
        MyBase.OnKeyDown(e)
    End Sub

End Class

Implementasi code-behind dari penangan peristiwa kelas penimpaan dalam sampel kode ini dibahas di bagian berikutnya.

Mengambil alih penanganan aktivitas kelas

Beberapa kelas dasar elemen visual mengekspos kosong Pada<nama> peristiwa dan metode virtual nama> peristiwa OnPreview<untuk setiap peristiwa input yang dirutekan publik mereka. Misalnya, UIElement mengimplementasikan OnKeyDown penanganan aktivitas virtual dan OnPreviewKeyDown , dan banyak lainnya. Anda dapat mengambil alih penanganan aktivitas virtual kelas dasar untuk menerapkan penangan aktivitas kelas penggantian untuk kelas turunan Anda. Misalnya, Anda dapat menambahkan penangan kelas penimpaan untuk DragEnter peristiwa di kelas turunan OnDragEnter apa pun UIElement dengan mengambil alih metode virtual. Mengesampingkan metode virtual kelas dasar adalah cara yang lebih sederhana untuk mengimplementasikan penangan kelas daripada mendaftarkan penangan kelas dalam konstruktor statis. Dalam penimpaan, Anda dapat menaikkan peristiwa, memulai logika khusus kelas untuk mengubah properti elemen pada instans, menandai peristiwa sebagai ditangani, atau melakukan logika penanganan peristiwa lainnya.

Tidak seperti penanganan aktivitas kelas statis, sistem peristiwa WPF hanya memanggil penangan peristiwa kelas ambil alih untuk kelas yang paling turunan dalam hierarki kelas. Kelas yang paling turunan dalam hierarki kelas, kemudian dapat menggunakan kata kunci dasar untuk memanggil implementasi dasar metode virtual. Dalam kebanyakan kasus, Anda harus memanggil implementasi dasar, terlepas dari apakah Anda menandai peristiwa sebagai ditangani. Anda hanya boleh menghilangkan panggilan implementasi dasar jika kelas Anda memiliki persyaratan untuk mengganti logika implementasi dasar, jika ada. Apakah Anda memanggil implementasi dasar sebelum atau sesudah kode penimpaan tergantung pada sifat implementasi Anda.

Dalam sampel kode sebelumnya, metode virtual kelas OnKeyDown dasar ditimpa di kelas ComponentWrapper dan ComponentWrapperBase . Karena sistem peristiwa WPF hanya memanggil ComponentWrapper.OnKeyDown penanganan aktivitas kelas ambil alih, handler tersebut ComponentWrapperBase.OnKeyDown menggunakan base.OnKeyDown(e) untuk memanggil penanganan aktivitas kelas penimpaanbase.OnKeyDown(e), yang pada gilirannya menggunakan untuk memanggil StackPanel.OnKeyDown metode virtual. Urutan peristiwa dalam sampel kode sebelumnya adalah:

  1. Handler instans yang terpasang componentWrapper dipicu oleh peristiwa yang dirutekan PreviewKeyDown .
  2. Handler kelas statis yang terpasang componentWrapper dipicu oleh peristiwa yang dirutekan KeyDown .
  3. Handler kelas statis yang terpasang componentWrapperBase dipicu oleh peristiwa yang dirutekan KeyDown .
  4. Penangan kelas ambil alih yang dilampirkan componentWrapper dipicu oleh peristiwa yang dirutekan KeyDown .
  5. Penangan kelas ambil alih yang dilampirkan componentWrapperBase dipicu oleh peristiwa yang dirutekan KeyDown .
  6. Peristiwa KeyDown yang dirutekan ditandai sebagai ditangani.
  7. Handler instans yang terpasang componentWrapper dipicu oleh peristiwa yang dirutekan KeyDown . Handler terdaftar dengan parameter yang handledEventsToo diatur ke true.

Penekanan peristiwa input dalam kontrol komposit

Beberapa kontrol komposit menekan peristiwa input di tingkat komponen untuk menggantinya dengan peristiwa tingkat tinggi yang disesuaikan yang membawa lebih banyak informasi atau menyiratkan perilaku yang lebih spesifik. Kontrol komposit terdiri dari beberapa kontrol praktis atau kelas dasar kontrol. Contoh klasik adalah Button kontrol, yang mengubah berbagai peristiwa mouse menjadi peristiwa yang Click dirutekan. Kelas Button dasar adalah ButtonBase, yang secara tidak langsung berasal dari UIElement. Sebagian besar infrastruktur peristiwa yang diperlukan untuk pemrosesan input kontrol tersedia di tingkat tersebut UIElement . UIElement mengekspos beberapa Mouse peristiwa seperti MouseLeftButtonDown dan MouseRightButtonDown. UIElement juga mengimplementasikan metode OnMouseLeftButtonDown virtual kosong dan OnMouseRightButtonDown sebagai penangan kelas yang telah terdaftar sebelumnya. ButtonBase mengambil alih handler kelas ini, dan dalam handler penimpaan Handled mengatur properti ke true dan menaikkan Click peristiwa. Hasil akhirnya untuk sebagian besar pendengar adalah peristiwa MouseLeftButtonDown dan MouseRightButtonDown disembunyikan, dan peristiwa tingkat Click tinggi terlihat.

Bekerja di sekitar penekanan peristiwa input

Terkadang penekanan peristiwa dalam kontrol individual dapat mengganggu logika penanganan peristiwa di aplikasi Anda. Misalnya, jika aplikasi Anda menggunakan sintaks atribut XAML untuk melampirkan handler untuk MouseLeftButtonDown peristiwa pada elemen akar XAML, handler tersebut Button tidak akan dipanggil karena kontrol menandai MouseLeftButtonDown peristiwa sebagai ditangani. Jika Anda ingin elemen ke akar aplikasi Anda dipanggil untuk peristiwa rute yang ditangani, Anda dapat:

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

  • Jika peristiwa yang ditandai sebagai ditangani adalah peristiwa input gelembung, lampirkan handler untuk peristiwa pratinjau yang dipasangkan, jika tersedia. Misalnya, jika kontrol menekan MouseLeftButtonDown peristiwa, Anda dapat melampirkan handler untuk peristiwa sebagai gantinya PreviewMouseLeftButtonDown . Pendekatan ini hanya berfungsi untuk pratinjau dan pasangan peristiwa input gelembung, yang berbagi data peristiwa. Berhati-hatilah untuk tidak menandai seperti yang ditangani PreviewMouseLeftButtonDown karena itu akan sepenuhnya menekan Click peristiwa.

Untuk contoh cara mengatasi penekanan peristiwa input, lihat Mengatasi penindasan peristiwa menurut kontrol.

Lihat juga