Bagikan melalui


Menandai Peristiwa Yang Dirutekan sebagai Ditangani, dan Penanganan Kelas

Handler untuk peristiwa yang dirutekan dapat menandai peristiwa yang ditangani dalam data peristiwa. Menangani peristiwa akan secara efektif mempersingkat rute. Penanganan kelas adalah konsep pemrograman yang didukung oleh peristiwa yang dirutekan. Handler kelas memiliki kesempatan untuk menangani peristiwa rute tertentu di tingkat kelas dengan handler yang dipanggil sebelum handler instans apa pun pada instans kelas apa pun.

Prasyarat

Topik ini menguraikan konsep yang diperkenalkan dalam Gambaran Umum Peristiwa Yang Dirutekan.

Kapan Menandai Peristiwa sebagai Ditangani

Saat Anda mengatur nilai Handled properti ke true dalam data peristiwa untuk peristiwa yang dirutekan, ini disebut sebagai "menandai peristiwa yang ditangani". Tidak ada aturan absolut ketika Anda harus menandai peristiwa yang dirutekan sebagai ditangani, baik sebagai penulis aplikasi, atau sebagai penulis kontrol yang merespons peristiwa yang dirutekan yang ada atau menerapkan peristiwa rute baru. Sebagian besar, konsep "ditangani" seperti yang dibawa dalam data peristiwa yang dirutekan harus digunakan sebagai protokol terbatas untuk respons aplikasi Anda sendiri terhadap berbagai peristiwa rute yang diekspos dalam API WPF serta untuk setiap peristiwa yang dirutekan kustom. Cara lain untuk mempertimbangkan masalah "ditangani" adalah Anda umumnya harus menandai peristiwa yang dirutekan yang ditangani jika kode Anda merespons peristiwa yang dirutekan dengan cara yang signifikan dan relatif lengkap. Biasanya, tidak boleh ada lebih dari satu respons signifikan yang memerlukan implementasi handler terpisah untuk setiap kejadian peristiwa yang dirutekan tunggal. Jika diperlukan lebih banyak respons, maka kode yang diperlukan harus diimplementasikan melalui logika aplikasi yang ditautkan dalam satu handler daripada dengan menggunakan sistem peristiwa yang dirutekan untuk penerusan. Konsep apa yang "signifikan" juga subjektif, dan tergantung pada aplikasi atau kode Anda. Sebagai panduan umum, beberapa contoh "respons signifikan" meliputi: mengatur fokus, memodifikasi status publik, mengatur properti yang memengaruhi representasi visual, dan meningkatkan peristiwa baru lainnya. Contoh respons yang tidak signifikan meliputi: memodifikasi status privat (tanpa dampak visual, atau representasi terprogram), pengelogan peristiwa, atau melihat argumen peristiwa dan memilih untuk tidak meresponsnya.

Perilaku sistem peristiwa yang dirutekan memperkuat model "respons signifikan" ini untuk menggunakan status peristiwa yang ditangani dari peristiwa yang dirutekan, karena handler yang ditambahkan di XAML atau tanda tangan AddHandler umum tidak dipanggil sebagai respons terhadap peristiwa yang dirutekan di mana data peristiwa sudah ditandai ditangani. Anda harus melalui upaya ekstra untuk menambahkan handler dengan handledEventsToo versi parameter (AddHandler(RoutedEvent, Delegate, Boolean)) untuk menangani peristiwa rute yang ditandai ditangani oleh peserta sebelumnya dalam rute peristiwa.

Dalam beberapa keadaan, kontrol itu sendiri menandai peristiwa tertentu yang dirutekan seperti yang ditangani. Peristiwa rute yang ditangani mewakili keputusan oleh penulis kontrol WPF bahwa tindakan kontrol sebagai respons terhadap peristiwa yang dirutekan signifikan atau lengkap sebagai bagian dari implementasi kontrol, dan peristiwa tidak memerlukan penanganan lebih lanjut. Biasanya ini dilakukan dengan menambahkan handler kelas untuk suatu peristiwa, atau dengan menimpa salah satu virtual handler kelas yang ada di kelas dasar. Anda masih dapat mengatasi penanganan peristiwa ini jika perlu; lihat Mengatasi Penekanan Peristiwa berdasarkan Kontrol nanti dalam topik ini.

Peristiwa "Pratinjau" (Penerowongan) vs. Peristiwa Gelembung, dan Penanganan Peristiwa

Pratinjau peristiwa yang dirutekan adalah peristiwa yang mengikuti rute penerowongan melalui pohon elemen. "Pratinjau" yang dinyatakan dalam konvensi penamaan menunjukkan prinsip umum untuk peristiwa input bahwa pratinjau (penerowongan) peristiwa yang dirutekan dinaikkan sebelum peristiwa rute gelembung yang setara. Selain itu, input peristiwa yang dirutekan yang memiliki pasangan penerowongan dan gelembung memiliki logika penanganan yang berbeda. Jika peristiwa terowongan/pratinjau yang dirutekan ditandai sebagai ditangani oleh pendengar peristiwa, maka peristiwa yang dirutekan menggelegak akan ditandai ditangani bahkan sebelum pendengar peristiwa rute gelembung menerimanya. Peristiwa terowongan dan gelembung yang dirutekan secara teknis memisahkan peristiwa, tetapi sengaja berbagi instans data peristiwa yang sama untuk mengaktifkan perilaku ini.

Koneksi antara penerowongan dan peristiwa yang dirutekan bergelombang dicapai oleh implementasi internal tentang bagaimana kelas WPF tertentu menaikkan peristiwa rute yang dinyatakan sendiri, dan ini benar dari peristiwa yang dirutekan input yang dipasangkan. Tetapi kecuali implementasi tingkat kelas ini ada, tidak ada koneksi antara peristiwa terowongan yang dirutekan dan peristiwa rute yang menggelegak yang berbagi skema penamaan: tanpa implementasi seperti itu, mereka akan menjadi dua peristiwa yang dirutekan sepenuhnya terpisah dan tidak akan dinaikkan secara berurutan atau berbagi data peristiwa.

Untuk informasi selengkapnya tentang cara menerapkan pasangan peristiwa yang dirutekan input terowongan/gelembung di kelas kustom, lihat Membuat Peristiwa Rute Kustom.

Penangan Kelas dan Penangan Instans

Peristiwa yang dirutekan mempertimbangkan dua jenis pendengar yang berbeda ke acara: pendengar kelas dan pendengar instans. Pendengar kelas ada karena jenis telah memanggil API tertentu EventManager ,RegisterClassHandler dalam konstruktor statisnya, atau telah menimpa metode virtual penangan kelas dari kelas dasar elemen. Pendengar instans adalah instans/elemen kelas tertentu di mana satu atau beberapa handler telah dilampirkan untuk peristiwa yang dirutekan tersebut dengan panggilan ke AddHandler. Peristiwa yang dirutekan WPF yang ada melakukan panggilan sebagai AddHandler bagian dari pembungkus peristiwa runtime bahasa umum (CLR) menambahkan{} dan menghapus{} implementasi peristiwa, yang juga bagaimana mekanisme XAML sederhana melampirkan penanganan peristiwa melalui sintaks atribut diaktifkan. Oleh karena itu bahkan penggunaan XAML sederhana pada akhirnya sama AddHandler dengan panggilan.

Elemen dalam pohon visual diperiksa untuk implementasi handler terdaftar. Handler berpotensi dipanggil di seluruh rute, dalam urutan yang melekat pada jenis strategi perutean untuk peristiwa yang dirutekan tersebut. Misalnya, peristiwa yang dirutekan gelembung pertama-tama akan memanggil handler yang dilampirkan ke elemen yang sama yang menaikkan peristiwa yang dirutekan. Kemudian peristiwa yang dirutekan "gelembung" ke elemen induk berikutnya dan sebagainya sampai elemen akar aplikasi tercapai.

Dari perspektif elemen akar dalam rute gelembung, jika penanganan kelas atau elemen apa pun yang lebih dekat dengan sumber peristiwa yang dirutekan memanggil handler yang menandai argumen peristiwa sebagai ditangani, maka handler pada elemen root tidak dipanggil, dan rute peristiwa secara efektif dipersingkat sebelum mencapai elemen akar tersebut. Namun, rute tidak sepenuhnya dihentikan, karena handler dapat ditambahkan menggunakan kondisional khusus yang masih harus dipanggil, bahkan jika handler kelas atau handler instans telah menandai peristiwa yang dirutekan sebagai ditangani. Ini dijelaskan dalam Menambahkan Handler Instans yang Dimunculkan Bahkan Ketika Peristiwa Ditandai Ditangani, nanti dalam topik ini.

Pada tingkat yang lebih dalam dari rute peristiwa, ada juga beberapa penangan kelas yang berpotensi bertindak pada instans kelas tertentu. Ini karena model penanganan kelas untuk peristiwa yang dirutekan memungkinkan semua kelas yang mungkin dalam hierarki kelas untuk masing-masing mendaftarkan handler kelasnya sendiri untuk setiap peristiwa yang dirutekan. Setiap handler kelas ditambahkan ke penyimpanan internal, dan ketika rute peristiwa untuk aplikasi dibangun, penangan kelas semuanya ditambahkan ke rute peristiwa. Handler kelas ditambahkan ke rute sehingga handler kelas yang paling turunan dipanggil terlebih dahulu, dan handler kelas dari setiap kelas dasar berturut-turut dipanggil berikutnya. Umumnya, penangan kelas tidak terdaftar sedih sehingga mereka juga merespons peristiwa rute yang sudah ditandai ditangani. Oleh karena itu, mekanisme penanganan kelas ini memungkinkan salah satu dari dua pilihan:

  • Kelas turunan dapat melengkapi penanganan kelas yang diwariskan dari kelas dasar dengan menambahkan handler yang tidak menandai peristiwa yang dirutekan yang ditangani, karena handler kelas dasar akan dipanggil beberapa saat setelah handler kelas turunan.

  • Kelas turunan dapat menggantikan penanganan kelas dari kelas dasar dengan menambahkan penangan kelas yang menandai peristiwa yang dirutekan yang ditangani. Anda harus berhati-hati dengan pendekatan ini, karena akan berpotensi mengubah desain kontrol dasar yang dimaksudkan di area seperti tampilan visual, logika status, penanganan input, dan penanganan perintah.

Penanganan Kelas Peristiwa Yang Dirutekan berdasarkan Kelas Dasar Kontrol

Pada setiap node elemen yang diberikan dalam rute peristiwa, pendengar kelas memiliki kesempatan untuk merespons peristiwa yang dirutekan sebelum pendengar instans apa pun di elemen dapat. Untuk alasan ini, penangan kelas kadang-kadang digunakan untuk menekan peristiwa yang dirutekan bahwa implementasi kelas kontrol tertentu tidak ingin menyebar lebih lanjut, atau untuk memberikan penanganan khusus dari peristiwa yang dirutekan yang merupakan fitur kelas. Misalnya, kelas mungkin menaikkan peristiwa khusus kelasnya sendiri yang berisi lebih spesifik tentang arti beberapa kondisi input pengguna dalam konteks kelas tertentu tersebut. Implementasi kelas kemudian dapat menandai peristiwa yang dirutekan lebih umum seperti yang ditangani. Penangan kelas biasanya ditambahkan sed sehingga tidak dipanggil untuk peristiwa yang dirutekan di mana data peristiwa bersama sudah ditandai ditangani, tetapi untuk kasus atipikal ada juga RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) tanda tangan yang mendaftarkan penangan kelas untuk dipanggil bahkan ketika peristiwa yang dirutekan ditandai ditangani.

Virtual Penangan Kelas

Beberapa elemen, terutama elemen dasar seperti UIElement, mengekspos metode virtual "On*Event" dan "OnPreview*Event" kosong yang sesuai dengan daftar peristiwa yang dirutekan publik. Metode virtual ini dapat ditimpa untuk mengimplementasikan handler kelas untuk peristiwa yang dirutekan tersebut. Kelas elemen dasar mendaftarkan metode virtual ini sebagai penangan kelas mereka untuk setiap peristiwa yang dirutekan tersebut menggunakan RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) seperti yang dijelaskan sebelumnya. Metode virtual On*Event membuatnya jauh lebih mudah untuk menerapkan penanganan kelas untuk peristiwa yang dirutekan yang relevan, tanpa memerlukan inisialisasi khusus dalam konstruktor statis untuk setiap jenis. Misalnya, Anda dapat menambahkan penanganan kelas untuk DragEnter peristiwa di kelas turunan OnDragEnter apa pun UIElement dengan mengambil alih metode virtual. Dalam penimpaan, Anda dapat menangani peristiwa yang dirutekan, meningkatkan peristiwa lain, memulai logika khusus kelas yang mungkin mengubah properti elemen pada instans, atau kombinasi tindakan tersebut. Anda umumnya harus memanggil implementasi dasar dalam penimpaan tersebut bahkan jika Anda menandai peristiwa yang ditangani. Memanggil implementasi dasar sangat disarankan karena metode virtual ada di kelas dasar. Pola virtual standar yang dilindungi untuk memanggil implementasi dasar dari setiap virtual pada dasarnya menggantikan dan paralel mekanisme serupa yang asli untuk penanganan kelas peristiwa yang dirutekan, di mana penangan kelas untuk semua kelas dalam hierarki kelas dipanggil pada instans tertentu, dimulai dengan handler kelas yang paling turunan dan melanjutkan ke penangan kelas dasar. Anda hanya boleh menghilangkan panggilan implementasi dasar jika kelas Anda memiliki persyaratan yang disengaja untuk mengubah logika penanganan kelas dasar. Apakah Anda memanggil implementasi dasar sebelum atau sesudah kode penimpaan Anda akan bergantung pada sifat implementasi Anda.

Penanganan Kelas Peristiwa Input

Metode virtual handler kelas semuanya terdaftar sedih sehingga hanya dipanggil dalam kasus di mana data peristiwa bersama belum ditandai ditangani. Selain itu, untuk peristiwa input secara unik, penerowongan dan versi gelembung biasanya dinaikkan secara berurutan dan berbagi data peristiwa. Ini mengharuskan bahwa untuk sepasang penangan kelas tertentu dari peristiwa input di mana salah satunya adalah versi penerowongan dan yang lain adalah versi gelembung, Anda mungkin tidak ingin menandai peristiwa yang ditangani segera. Jika Anda menerapkan metode virtual penanganan kelas penerowongan untuk menandai peristiwa yang ditangani, itu akan mencegah penanganan kelas gelembung dipanggil (serta mencegah penangan instans yang biasanya terdaftar untuk peristiwa penerowongan atau gelembung dipanggil).

Setelah penanganan kelas pada node selesai, listener instans dipertimbangkan.

Menambahkan Handler Instans yang Dimunculkan Bahkan Ketika Peristiwa Ditandai Ditangani

Metode ini AddHandler menyediakan kelebihan beban tertentu yang memungkinkan Anda menambahkan handler yang akan dipanggil oleh sistem peristiwa setiap kali peristiwa mencapai elemen penanganan dalam rute, bahkan jika beberapa handler lain telah menyesuaikan data peristiwa untuk menandai peristiwa tersebut sebagai ditangani. Ini biasanya tidak dilakukan. Umumnya, handler dapat ditulis untuk menyesuaikan semua area kode aplikasi yang mungkin dipengaruhi oleh suatu peristiwa, terlepas dari di mana ia ditangani di pohon elemen, bahkan jika beberapa hasil akhir diinginkan. Juga, biasanya hanya ada satu elemen yang perlu merespons peristiwa itu, dan logika aplikasi yang sesuai telah terjadi. handledEventsToo Tetapi kelebihan beban tersedia untuk kasus luar biasa di mana beberapa elemen lain di pohon elemen atau komposit kontrol telah menandai peristiwa sebagai ditangani, tetapi elemen lain baik lebih tinggi atau lebih rendah di pohon elemen (tergantung pada rute) masih ingin memiliki handler mereka sendiri dipanggil.

Kapan Menandai Peristiwa yang Ditangani sebagai Tidak Tertangani

Umumnya, peristiwa yang dirutekan yang ditandai ditangani tidak boleh ditandai tidak tertangani (Handled diatur kembali ke false) bahkan oleh handler yang bertindak pada handledEventsToo. Namun, beberapa peristiwa input memiliki representasi peristiwa tingkat tinggi dan tingkat bawah yang dapat tumpang tindih ketika peristiwa tingkat tinggi terlihat pada satu posisi di pohon dan peristiwa tingkat rendah pada posisi lain. Misalnya, pertimbangkan kasus di mana elemen turunan mendengarkan peristiwa kunci tingkat tinggi seperti TextInput saat elemen induk mendengarkan peristiwa tingkat rendah seperti KeyDown. Jika elemen induk menangani peristiwa tingkat rendah, peristiwa tingkat yang lebih tinggi dapat ditekan bahkan di elemen anak yang secara intuitif harus memiliki kesempatan pertama untuk menangani peristiwa.

Dalam situasi ini, mungkin perlu menambahkan handler ke elemen induk dan elemen turunan untuk peristiwa tingkat rendah. Implementasi handler elemen turunan dapat menandai peristiwa tingkat rendah seperti yang ditangani, tetapi implementasi handler elemen induk akan mengaturnya tidak tertangani lagi sehingga elemen lebih lanjut ke atas pohon (serta peristiwa tingkat tinggi) dapat memiliki kesempatan untuk merespons. Situasi ini seharusnya cukup jarang terjadi.

Sengaja Menekan Peristiwa Input untuk Pengomposisian Kontrol

Skenario utama di mana penanganan kelas peristiwa yang dirutekan digunakan adalah untuk peristiwa input dan kontrol yang disusupi. Kontrol yang dikomposisikan adalah dengan definisi yang terdiri dari beberapa kontrol praktis atau kelas dasar kontrol. Seringkali penulis kontrol ingin mengampuni semua kemungkinan peristiwa input yang mungkin dinaikkan oleh masing-masing subkomponen, untuk melaporkan seluruh kontrol sebagai sumber peristiwa tunggal. Dalam beberapa kasus, penulis kontrol mungkin ingin menekan peristiwa dari komponen sepenuhnya, atau mengganti peristiwa yang ditentukan komponen yang membawa informasi lebih lanjut atau menyiratkan perilaku yang lebih spesifik. Contoh kanonis yang segera terlihat oleh penulis komponen apa pun adalah bagaimana Windows Presentation Foundation (WPF) Button menangani peristiwa mouse apa pun yang akhirnya akan mengatasi peristiwa intuitif yang dimiliki semua tombol: peristiwa Click .

Kelas Button dasar (ButtonBase) berasal dari Control yang pada gilirannya berasal dari FrameworkElement dan UIElement, dan banyak infrastruktur peristiwa yang diperlukan untuk pemrosesan input kontrol tersedia di tingkat tersebut UIElement . Secara khusus, UIElement memproses peristiwa umum Mouse yang menangani pengujian hit untuk kursor mouse dalam batas-batasnya, dan menyediakan peristiwa yang berbeda untuk tindakan tombol yang paling umum, seperti MouseLeftButtonDown. UIElement juga menyediakan virtual OnMouseLeftButtonDown kosong sebagai handler kelas yang telah didaftarkan sebelumnya untuk MouseLeftButtonDown, dan ButtonBase mengambil alihnya. Demikian pula, ButtonBase menggunakan handler kelas untuk MouseLeftButtonUp. Dalam penimpaan, yang diteruskan data peristiwa, implementasi menandai instans tersebut seperti yang RoutedEventArgs ditangani dengan mengatur Handled ke true, dan data peristiwa yang sama adalah apa yang berlanjut di sepanjang sisa rute ke handler kelas lain dan juga untuk penangan instans atau setter peristiwa. Selain itu, penimpaan OnMouseLeftButtonUp selanjutnya akan meningkatkan Click peristiwa. Hasil akhirnya bagi sebagian besar pendengar adalah bahwa MouseLeftButtonDown peristiwa dan MouseLeftButtonUp "menghilang" dan digantikan sebagai gantinya oleh Click, peristiwa yang menyimpan lebih banyak arti karena diketahui bahwa peristiwa ini berasal dari tombol benar dan bukan beberapa bagian komposit dari tombol atau dari beberapa elemen lain sepenuhnya.

Mengatasi Penekanan Peristiwa berdasarkan Kontrol

Terkadang perilaku penindasan peristiwa ini dalam kontrol individu dapat mengganggu beberapa niat logika penanganan peristiwa yang lebih umum untuk aplikasi Anda. Misalnya, jika karena alasan tertentu aplikasi Anda memiliki handler untuk MouseLeftButtonDown terletak di elemen akar aplikasi, Anda akan melihat bahwa setiap klik mouse pada tombol tidak akan memanggil MouseLeftButtonDown atau MouseLeftButtonUp handler di tingkat akar. Peristiwa itu sendiri sebenarnya memang menggelegak (sekali lagi, rute peristiwa tidak benar-benar berakhir, tetapi sistem peristiwa yang dirutekan mengubah perilaku pemanggilan handler mereka setelah ditandai ditangani). Ketika peristiwa yang dirutekan mencapai tombol, ButtonBase penanganan kelas menandai yang MouseLeftButtonDown ditangani karena ingin mengganti Click peristiwa dengan lebih banyak arti. Oleh karena itu, setiap handler standar MouseLeftButtonDown lebih jauh ke rute tidak akan dipanggil. Ada dua teknik yang dapat Anda gunakan untuk memastikan bahwa handler Anda akan dipanggil dalam keadaan ini.

Teknik pertama adalah dengan sengaja menambahkan handler menggunakan handledEventsToo tanda tangan AddHandler(RoutedEvent, Delegate, Boolean). Batasan pendekatan ini adalah bahwa teknik untuk melampirkan penanganan aktivitas hanya dimungkinkan dari kode, bukan dari markup. Sintaks sederhana menentukan nama penanganan aktivitas sebagai nilai atribut peristiwa melalui Extensible Application Markup Language (XAML) tidak mengaktifkan perilaku tersebut.

Teknik kedua hanya berfungsi untuk peristiwa input, di mana versi penerowongan dan gelembung dari peristiwa yang dirutekan dipasangkan. Untuk peristiwa yang dirutekan ini, Anda dapat menambahkan handler ke pratinjau/penerowongan peristiwa rute yang setara sebagai gantinya. Peristiwa yang dirutekan itu akan terowongan melalui rute mulai dari akar, sehingga kode penanganan kelas tombol tidak akan mencegatnya, dengan anggapan Anda melampirkan handler Pratinjau di beberapa tingkat elemen leluhur di pohon elemen aplikasi. Jika Anda menggunakan pendekatan ini, berhati-hatilah dengan menandai peristiwa Pratinjau apa pun yang ditangani. Untuk contoh yang diberikan dengan PreviewMouseLeftButtonDown ditangani pada elemen akar, jika Anda menandai peristiwa seperti Handled dalam implementasi handler, Anda benar-benar akan menekan Click peristiwa. Biasanya itu bukan perilaku yang diinginkan.

Baca juga