Yönlendirilen olayları işlenmiş olarak işaretleme ve sınıf işleme (WPF .NET)

Yönlendirilmiş bir olayı işlenmiş olarak işaretlemek için mutlak bir kural olmasa da, kodunuz olaya önemli bir şekilde yanıt veriyorsa bir olayı işlenmiş olarak işaretlemeyi göz önünde bulundurun. İşlendi olarak işaretlenen yönlendirilmiş bir olay, yolu boyunca devam eder, ancak yalnızca işlenen olaylara yanıt verecek şekilde yapılandırılmış işleyiciler çağrılır. Temel olarak, yönlendirilen bir olayı işlenmiş olarak işaretlemek, görünürlüğünü olay yolu boyunca dinleyicilere sınırlar.

Yönlendirilen olay işleyicileri örnek işleyicileri veya sınıf işleyicileri olabilir. Örnek işleyicileri, nesneler veya XAML öğelerinde yönlendirilen olayları işler. Sınıf işleyicileri, sınıf düzeyinde yönlendirilmiş bir olayı işler ve sınıfın herhangi bir örneğinde aynı olaya yanıt veren herhangi bir örnek işleyicisi çağrılır. Yönlendirilen olaylar işlendi olarak işaretlendiğinde, bunlar genellikle sınıf işleyicileri içinde bu şekilde işaretlenir. Bu makalede, yönlendirilen olayları işlenmek üzere işaretlemenin avantajları ve olası tuzakları, farklı türlerdeki yönlendirilmiş olaylar ve yönlendirilen olay işleyicileri ve bileşik denetimlerde olay gizleme işlemleri ele alınmaktadır.

Önemli

.NET 7 ve .NET 6 için Masaüstü Kılavuzu belgeleri yapım aşamasındadır.

Ön koşullar

Makalede, yönlendirilen olaylar hakkında temel bilgiler edindiğiniz ve Yönlendirilen olaylara genel bakış makalesini okuduğunuz varsayılır. Bu makaledeki örnekleri takip etmek için, Genişletilebilir Uygulama biçimlendirme dili (XAML) hakkında bilgi sahibi olmanız ve Windows Presentation Foundation (WPF) uygulamalarının nasıl yazıldığından haberdar olmanız yardımcı olur.

Yönlendirilen olaylar ne zaman işlenmiş olarak işaretlenmeli?

Genellikle, yönlendirilen her olay için yalnızca bir işleyici önemli bir yanıt sağlamalıdır. Birden çok işleyici arasında önemli bir yanıt sağlamak için yönlendirilmiş olay sistemini kullanmaktan kaçının. Önemli bir yanıtı oluşturan şeyin tanımı özneldir ve uygulamanıza bağlıdır. Genel rehberlik olarak:

  • Önemli yanıtlar arasında odağı ayarlama, genel durumu değiştirme, görsel gösterimi etkileyen özellikleri ayarlama, yeni olaylar oluşturma ve bir olayı tamamen işleme sayılabilir.
  • Önemsiz yanıtlar, görsel veya programlı etki olmadan özel durumu değiştirmeyi, olay günlüğünü ve olaya yanıt vermeden olay verilerini incelemeyi içerir.

Bazı WPF denetimleri, işlenmiş olarak işaretleyerek daha fazla işleme gerektirmeyen bileşen düzeyinde olayları gizler. Bir denetim tarafından işlendi olarak işaretlenmiş bir olayı işlemek istiyorsanız bkz . Denetimler tarafından olay gizlemeye geçici bir çözüm.

Bir olayı işlenmiş olarak işaretlemek için, olay verilerindeki özellik değerini olarak trueayarlayınHandled. Bu değeri falsedeğerine geri döndürmek mümkün olsa da, bunu yapmak nadir olmalıdır.

Yönlendirilen olay çiftlerini önizleme ve kabarcık oluşturma

Yönlendirilen olay çiftlerinin önizlemesi ve kabarcıkları giriş olaylarına özeldir. Çeşitli giriş olayları, ve gibi bir tünel oluşturma ve yönlendirilmiş olay çifti kabarcıklarıPreviewKeyDown uygular.KeyDown Ön ek, Preview önizleme olayı tamamlandıktan sonra kabarcıklanma olayının başlatıldığını belirtir. Her önizleme ve kabarcık oluşturma olay çifti, olay verilerinin aynı örneğini paylaşır.

Yönlendirilen olay işleyicileri, bir olayın yönlendirme stratejisine karşılık gelen bir sırayla çağrılır:

  1. Önizleme olayı, uygulama kök öğesinden yönlendirilen olayı tetikleyen öğeye doğru ilerler. Uygulama kök öğesine eklenen önizleme olay işleyicileri önce çağrılır, ardından ardışık iç içe geçmiş öğelere eklenmiş işleyiciler kullanılır.
  2. Önizleme olayı tamamlandıktan sonra eşleştirilmiş kabarcıklama olayı, yönlendirilen olayı uygulama kök öğesine yükselten öğeden hareket eder. Yönlendirilen olayı tetikleyen aynı öğeye bağlı olan kabarcık olay işleyicileri önce çağrılır, ardından ardışık üst öğelere eklenmiş işleyiciler.

Eşleştirilmiş önizleme ve kabarcıklama olayları, kendi yönlendirilmiş olaylarını bildiren ve oluşturan çeşitli WPF sınıflarının iç uygulamasının bir parçasıdır. Bu sınıf düzeyinde iç uygulama olmadan, yönlendirilen olayların önizlemesi ve kabarcıkları tamamen ayrıdır ve olay adlandırmadan bağımsız olarak olay verilerini paylaşmaz. Özel bir sınıfta kabarcıklama veya tünel oluşturma giriş yönlendirilmiş olayları uygulama hakkında bilgi için bkz . Özel yönlendirilmiş olay oluşturma.

Her önizleme ve kabarcıklama olay çifti olay verilerinin aynı örneğini paylaştığından, önizlemeye yönlendirilen bir olay işlendi olarak işaretlenirse eşleştirilmiş kabarcıklama olayı da işlenir. Kabarcıklı yönlendirilmiş bir olay işlendi olarak işaretlenirse, önizleme olayı tamamlandığından eşleştirilmiş önizleme olayını etkilemez. Önizlemeyi işaretlerken ve giriş olay çiftlerini işlenirken dikkatli olun. İşlenen bir önizleme giriş olayı tünel yolunun geri kalanı için normal olarak kaydedilmiş olay işleyicilerini çağırmaz ve eşleştirilmiş kabarcıklama olayı tetiklenmez. İşlenen bir kabarcık giriş olayı, kabarcık oluşturma yolunun geri kalanı için normal olarak kaydedilmiş olay işleyicileri çağırmaz.

Örnek ve sınıf tarafından yönlendirilen olay işleyicileri

Yönlendirilen olay işleyicileri örnek işleyicileri veya sınıf işleyicileri olabilir. Belirli bir sınıf için sınıf işleyicileri, bu sınıfın herhangi bir örneğinde aynı olaya yanıt veren herhangi bir örnek işleyiciden önce çağrılır. Bu davranış nedeniyle, yönlendirilen olaylar işlendi olarak işaretlendiğinde genellikle sınıf işleyicileri içinde bu şekilde işaretlenir. İki tür sınıf işleyicisi vardır:

Örnek olay işleyicileri

Doğrudan yöntemini çağırarak nesnelere veya XAML öğelerine AddHandler örnek işleyicileri ekleyebilirsiniz. WPF yönlendirilmiş olaylar, olay işleyicileri eklemek için yöntemini kullanan AddHandler ortak dil çalışma zamanı (CLR) olay sarmalayıcısını uygular. Olay işleyicileri eklemeye yönelik XAML özniteliği söz dizimi CLR olay sarmalayıcısına çağrıyla sonuçlandığından, XAML'de işleyicilerin eklenmesi bile bir AddHandler çağrıya çözümleniyor. İşlenen olaylar için:

  • XAML öznitelik söz dizimi veya ortak imzası AddHandler kullanılarak eklenen işleyiciler çağrılmıyor.
  • parametresi ayarlanmış true aşırı yükleme handledEventsToo kullanılarak AddHandler(RoutedEvent, Delegate, Boolean) eklenen işleyiciler çağrılır. İşlenen olaylara yanıt vermek gerektiğinde bu aşırı yükleme nadir durumlarda kullanılabilir. Örneğin, öğe ağacındaki bazı öğeler bir olayı işlendi olarak işaretlemiştir, ancak olay yolu boyunca daha ileriki diğer öğelerin işlenen olaya yanıt vermesi gerekir.

Aşağıdaki XAML örneği, adlı bir öğesini adlandırılmış componentWrapperbir öğesine sarmalayan TextBoxcomponentTextBoxadlı outerStackPanelözel bir StackPanel denetim ekler. Olay için PreviewKeyDown bir örnek olay işleyicisi, using XAML özniteliği söz dizimine eklenir componentWrapper . Sonuç olarak, örnek işleyicisi yalnızca tarafından componentTextBoxtetiklenen işlenmeyen PreviewKeyDown tünel olaylarına yanıt verir.

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

MainWindow Oluşturucu, parametresi olarak ayarlanmış şekilde aşırı yükleme handledEventsToo kullanarak UIElement.AddHandler(RoutedEvent, Delegate, Boolean) öğesine kabarcıklama olayı componentWrapper için KeyDown bir örnek işleyicisi trueekler. Sonuç olarak, örnek olay işleyicisi hem işlenmeyen hem de işlenen olaylara yanıt verir.

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

arka planda kod uygulaması ComponentWrapper sonraki bölümde gösterilir.

Statik sınıf olay işleyicileri

Bir sınıfın statik oluşturucusunda yöntemini çağırarak RegisterClassHandler statik sınıf olay işleyicileri ekleyebilirsiniz. Sınıf hiyerarşisindeki her sınıf, yönlendirilen her olay için kendi statik sınıf işleyicisini kaydedebilir. Sonuç olarak, olay yolundaki belirli bir düğümde aynı olay için çağrılan birden çok statik sınıf işleyicisi olabilir. Olay için olay yolu oluşturulduğunda, her düğüm için tüm statik sınıf işleyicileri olay yoluna eklenir. Bir düğümdeki statik sınıf işleyicilerinin çağrılma sırası, en çok türetilen statik sınıf işleyicisiyle başlar ve ardından ardışık her temel sınıftan statik sınıf işleyicileri gelir.

parametresi ayarlı true aşırı yükleme kullanılarak RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) kaydedilen statik sınıf olay işleyicileri hem işlenmeyen handledEventsToo hem de işlenen yönlendirilmiş olaylara yanıt verir.

Statik sınıf işleyicileri genellikle yalnızca işlenmeyen olaylara yanıt vermek için kaydedilir. Bu durumda, düğümdeki türetilmiş bir sınıf işleyicisi bir olayı işlendi olarak işaretlerse, bu olay için temel sınıf işleyicileri çağrılmayacak. Bu senaryoda temel sınıf işleyicisi, türetilmiş sınıf işleyicisi tarafından etkili bir şekilde değiştirilir. Temel sınıf işleyicileri genellikle görsel görünüm, durum mantığı, giriş işleme ve komut işleme gibi alanlarda denetim tasarımına katkıda bulunur, bu nedenle bunları değiştirme konusunda dikkatli olun. Bir olayı işlendi olarak işaretlemeyen türetilmiş sınıf işleyicileri, bunları değiştirmek yerine temel sınıf işleyicilerini tamamlar.

Aşağıdaki kod örneği, önceki XAML'de başvuruda bulunan özel denetimin sınıf hiyerarşisini ComponentWrapper gösterir. sınıfı ComponentWrapper sınıfından ComponentWrapperBase türetilir ve bu da sınıfından StackPanel türetilir. RegisterClassHandler ve ComponentWrapperBase sınıflarının statik oluşturucusunda kullanılan yöntemi, bu sınıfların ComponentWrapper her biri için statik sınıf olay işleyicisi kaydeder. WPF olay sistemi statik sınıf işleyicisinin ComponentWrapper önünde statik sınıf işleyicisini ComponentWrapperBase çağırır.

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

Bu kod örneğindeki geçersiz kılma sınıfı olay işleyicilerinin arka planda kod uygulaması sonraki bölümde ele alınmalıdır.

Sınıf olay işleyicilerini geçersiz kılma

Bazı görsel öğe temel sınıfları, genel yönlendirilmiş giriş olaylarının her biri için boş On olay adı> ve OnPreview<olay adı> sanal yöntemlerini kullanıma<sunar. Örneğin, UIElement ve OnPreviewKeyDown sanal olay işleyicilerini ve diğerlerini uygularOnKeyDown. Türetilmiş sınıflarınız için geçersiz kılma sınıfı olay işleyicilerini uygulamak için temel sınıf sanal olay işleyicilerini geçersiz kılabilirsiniz. Örneğin, sanal yöntemi geçersiz kılarak türetilmiş herhangi bir UIElement sınıfta olay için DragEnter bir geçersiz kılma sınıfı işleyicisi OnDragEnter ekleyebilirsiniz. Temel sınıf sanal yöntemlerini geçersiz kılma, sınıf işleyicilerini statik bir oluşturucuya kaydetmekten daha basit bir yoldur. Geçersiz kılma içinde olayları oluşturabilir, örneklerdeki öğe özelliklerini değiştirmek için sınıfa özgü mantık başlatabilir, olayı işlenmiş olarak işaretleyebilir veya başka olay işleme mantığı gerçekleştirebilirsiniz.

Statik sınıf olay işleyicilerinden farklı olarak WPF olay sistemi yalnızca sınıf hiyerarşisindeki en türetilmiş sınıf için geçersiz kılma sınıf olay işleyicilerini çağırır. Daha sonra bir sınıf hiyerarşisindeki en türetilmiş sınıf, sanal yöntemin temel uygulamasını çağırmak için base anahtar sözcüğünü kullanabilir. Çoğu durumda, bir olayı işlenmiş olarak işaretleyip işaretlemediğinize bakılmaksızın temel uygulamayı çağırmanız gerekir. Yalnızca sınıfınızın varsa temel uygulama mantığını değiştirme gereksinimi varsa temel uygulamayı çağırmayı atlamalısınız. Geçersiz kılma kodunuz öncesinde veya sonrasında temel uygulamayı çağırmak, uygulamanızın doğasına bağlıdır.

Yukarıdaki kod örneğinde, hem ve ComponentWrapperComponentWrapperBase sınıflarında temel sınıf OnKeyDown sanal yöntemi geçersiz kılınmış. WPF olay sistemi yalnızca geçersiz kılma sınıfı olay işleyicisini ComponentWrapper.OnKeyDown çağırdığından, bu işleyici geçersiz kılma sınıfı olay işleyicisini çağırmak ComponentWrapperBase.OnKeyDown için kullanır base.OnKeyDown(e) ve bu da sanal yöntemi çağırmak StackPanel.OnKeyDown için kullanırbase.OnKeyDown(e). Yukarıdaki kod örneğindeki olayların sırası:

  1. öğesine eklenen componentWrapper örnek işleyicisi, yönlendirilen olay tarafından PreviewKeyDown tetiklendi.
  2. öğesine eklenen componentWrapper statik sınıf işleyicisi, yönlendirilen olay tarafından KeyDown tetikleniyor.
  3. öğesine eklenen componentWrapperBase statik sınıf işleyicisi, yönlendirilen olay tarafından KeyDown tetikleniyor.
  4. öğesine eklenen componentWrapper geçersiz kılma sınıfı işleyicisi, yönlendirilen olay tarafından KeyDown tetikleniyor.
  5. öğesine eklenen componentWrapperBase geçersiz kılma sınıfı işleyicisi, yönlendirilen olay tarafından KeyDown tetikleniyor.
  6. Yönlendirilen KeyDown olay işlendi olarak işaretlenir.
  7. öğesine eklenen componentWrapper örnek işleyicisi, yönlendirilen olay tarafından KeyDown tetiklendi. İşleyici, parametresi olarak handledEventsToo ayarlanmış olarak truekaydedildi.

Bileşik denetimlerde giriş olayı gizleme

Bazı bileşik denetimler, daha fazla bilgi taşıyan veya daha belirli bir davranışa işaret eden özelleştirilmiş bir üst düzey olayla değiştirmek için bileşen düzeyinde giriş olaylarını gizler. Bileşik denetim, birden çok pratik denetimden veya denetim temel sınıfından oluşan bir tanımdır. Klasik bir örnek, çeşitli fare olaylarını yönlendirilmiş bir Click olaya dönüştüren denetimdirButton. Button Temel sınıf, dolaylı olarak öğesinden UIElementtüretilen şeklindedirButtonBase. Denetim girişi işleme için gereken olay altyapısının UIElement büyük bir kısmı düzeyinde kullanılabilir. UIElementve MouseRightButtonDowngibi MouseLeftButtonDown çeşitli Mouse olayları kullanıma sunar. UIElement ayrıca boş sanal yöntemleri OnMouseLeftButtonDown ve OnMouseRightButtonDown önceden kayıtlı sınıf işleyicileri olarak uygular. ButtonBasebu sınıf işleyicilerini geçersiz kılar ve geçersiz kılma işleyicisi içinde özelliğini olarak true ayarlar Handled ve bir Click olay oluşturur. Çoğu dinleyici için sonuç, ve MouseRightButtonDown olaylarının gizlenmesi MouseLeftButtonDown ve üst düzey Click olayın görünür olmasıdır.

Giriş olayı gizlemeyi geçici olarak çözme

Bazen tek tek denetimler içinde olay gizleme, uygulamanızdaki olay işleme mantığına müdahale edebilir. Örneğin, uygulamanız XAML kök öğesinde olay için bir işleyici eklemek için MouseLeftButtonDown XAML öznitelik söz dizimini kullandıysa, denetim olayı işlenmiş olarak işaretlediğinden ButtonMouseLeftButtonDown bu işleyici çağrılmayacak. İşlenen yönlendirilmiş olay için uygulamanızın köküne yönelik öğelerin çağrılmasını istiyorsanız şunları yapabilirsiniz:

  • parametresi olarak ayarlanmış yöntemi handledEventsToo çağırarak UIElement.AddHandler(RoutedEvent, Delegate, Boolean) işleyicileri trueekleyin. Bu yaklaşım, ekleyecek öğe için bir nesne başvurusu aldıktan sonra olay işleyicisinin arka planda koda eklenmesini gerektirir.

  • İşlendi olarak işaretlenen olay bir kabarcık giriş olayıysa, varsa eşleştirilmiş önizleme olayı için işleyiciler ekleyin. Örneğin, bir denetim olayı bastırıyorsa MouseLeftButtonDown , bunun yerine olay için PreviewMouseLeftButtonDown bir işleyici ekleyebilirsiniz. Bu yaklaşım yalnızca olay verilerini paylaşan giriş olay çiftlerini önizleme ve kabarcıklama için çalışır. Bu, olayı tamamen bastıracağı Click için öğesini işlenmiş olarak işaretlememeye PreviewMouseLeftButtonDown dikkat edin.

Giriş olayı gizleme sorununu geçici olarak çözme örneği için bkz . Denetimler tarafından olay gizlemeye geçici bir çözüm.

Ayrıca bkz.