次の方法で共有


添付イベントの概要 (WPF .NET)

拡張アプリケーション マークアップ言語 (XAML) は、添付イベントと呼ばれる言語コンポーネントとイベントの種類を定義します。 アタッチされたイベントを使用して、非要素クラスで新しい ルーティング イベント を定義し、ツリー内の任意の要素でそのイベントを発生させることができます。 これを行うには、添付イベントをルーティング イベントとして登録し、アタッチされたイベント機能をサポートする特定の バッキング コード を提供する必要があります。 添付イベントはルーティング イベントとして登録されるため、要素で発生すると、要素ツリーを通じて伝達されます。

前提 条件

この記事では、Windows Presentation Foundation (WPF) ルーティング イベントに関する基本的な知識を前提としており、WPFでルーティング イベントの概要 と XAML 読んだことを前提としています。 この記事の例に従うには、XAML に慣れている場合や、WPF アプリケーションを記述する方法を理解している場合に役立ちます。

添付イベントの構文

XAML 構文では、添付イベントはイベント名 と所有者の型 によって、<owner type>.<event name>の形式で指定されます。 イベント名は所有者型の名前で修飾されているため、構文により、インスタンス化できる任意の要素にイベントをアタッチできます。 この構文は、イベント ルートに沿って任意の要素にアタッチする通常のルーティング イベントのハンドラーにも適用できます。

次の XAML 属性構文は、AquariumFilter.Clean 添付イベントの AquariumFilter_Clean ハンドラーを aquarium1 要素にアタッチします。

<aqua:Aquarium x:Name="aquarium1" Height="300" Width="400" aqua:AquariumFilter.Clean="AquariumFilter_Clean"/>

この例では、AquariumFilter クラスと Aquarium クラスが別の共通言語ランタイム (CLR) 名前空間とアセンブリに存在するため、aqua: プレフィックスが必要です。

分離コードで添付イベントのハンドラーをアタッチすることもできます。 これを行うには、ハンドラーがアタッチする必要があるオブジェクトに対して AddHandler メソッドを呼び出し、イベント識別子とハンドラーをパラメーターとしてメソッドに渡します。

WPF で添付イベントを実装する方法

WPF 添付イベントは、RoutedEvent フィールドによってサポートされるルーティング イベントとして実装されます。 その結果、アタッチされたイベントは、発生した後に要素ツリーを通じて伝達されます。 一般に、アタッチされたイベントを発生させるオブジェクトは、イベント ソースと呼ばれ、システムまたはサービス ソースです。 システムまたはサービス ソースは、要素ツリーの直接的な部分ではありません。 他の添付イベントの場合、イベント ソースはツリー内の要素 (複合コントロール内のコンポーネントなど) である可能性があります。

添付イベントのシナリオ

WPF では、アタッチされたイベントは、サービス レベルの抽象化がある特定の機能領域で使用されます。 たとえば、WPF では、静的な Mouse または Validation クラスによって有効になっている添付イベントを使用します。 サービスとやり取りするクラスまたはサービスを使用するクラスは、アタッチされたイベント構文を使用してイベントと対話するか、添付イベントをルーティング イベントとして表示できます。 後者のオプションは、クラスがサービスの機能を統合する方法の一部です。

WPF 入力システムでは、添付イベントが広範囲に使用されます。 ただし、これらのアタッチされたイベントのほぼすべてが、基本要素を介して同等の非アタッチルーティング イベントとして表示されます。 ルーティングされた各入力イベントは基本要素クラスのメンバーであり、CLR イベント "ラッパー" でサポートされます。 アタッチされたイベントを直接使用または処理することはめったにありません。 たとえば、XAML またはコードビハインドで添付イベント構文を使用するよりも、相当する UIElement.MouseDown ルーティング イベントを使用して、UIElement の基になるアタッチされた Mouse.MouseDown イベントを処理する方が簡単です。

添付イベントは、入力デバイスの将来の拡張を可能にすることで、アーキテクチャの目的に役立ちます。 たとえば、新しい入力デバイスでは、マウス入力をシミュレートするために Mouse.MouseDown を発生させるだけで済み、Mouse から派生させる必要はありません。 添付イベントの XAML 処理は関連しないため、このシナリオにはイベントのコード処理が含まれます。

添付イベントを処理する

アタッチされたイベントをコーディングして処理するプロセスは、基本的に、アタッチされていないルーティング イベントの場合と同じです。

既に説明したように、既存の WPF 添付イベントは、通常、WPF で直接処理されることを意図していません。 多くの場合、添付イベントの目的は、複合コントロール内の要素がコントロール内の親要素にその状態を報告できるようにすることです。 そのシナリオでは、イベントはコードで発生し、関連する親クラスのクラス処理に依存します。 たとえば、Selector 内の項目は、Selected 添付イベントを発生させる必要があります。これは、Selector クラスによって処理されるクラスです。 Selector クラスは、Selected イベントを SelectionChanged ルーティング イベントに変換する可能性があります。 ルーティング イベントとクラス処理の詳細については、「ルーティング イベントを処理済みとしてマークする」および「クラス処理」を参照してください。

カスタム添付イベントを定義する

一般的な WPF 基本クラスから派生している場合は、クラスに 2 つのアクセサー メソッドを含めることで、カスタム添付イベントを実装できます。 これらのメソッドは次のとおりです。

  • Add<イベント名>Handler メソッド。イベント ハンドラーがアタッチされている要素である最初のパラメーターと、追加するイベント ハンドラーである 2 番目のパラメーターが含まれます。 メソッドは、戻り値なしで public および staticする必要があります。 このメソッドは、AddHandler 基底クラス メソッドを呼び出し、ルーティング イベントとハンドラーを引数として渡します。 このメソッドは、要素にイベント ハンドラーをアタッチするための XAML 属性構文をサポートしています。 このメソッドは、添付イベントのイベント ハンドラー ストアへのコード アクセスも可能にします。

  • Remove<イベント名>Handler メソッド。イベント ハンドラーがアタッチされている要素である最初のパラメーターと、削除するイベント ハンドラーである 2 番目のパラメーターが含まれます。 メソッドは、戻り値なしで public および staticする必要があります。 このメソッドは、RemoveHandler 基底クラス メソッドを呼び出し、ルーティング イベントとハンドラーを引数として渡します。 このメソッドは、アタッチされたイベントのイベント ハンドラー ストアへのコード アクセスを有効にします。

WPF は、RoutedEvent の識別子が WPF イベント システムによって定義されているため、添付イベントをルーティング イベントとして実装します。 また、イベントのルーティングは、添付イベントの XAML 言語レベルの概念の自然な拡張です。 この実装戦略では、アタッチされたイベントの処理を、UIElement 派生クラスまたは派生クラス ContentElement に制限します。これは、それらのクラスにのみ AddHandler 実装があるためです。

たとえば、次のコードでは、要素クラスではない AquariumFilter 所有者クラスに Clean 添付イベントを定義します。 このコードでは、添付イベントをルーティング イベントとして定義し、必要なアクセサー メソッドを実装します。

public class AquariumFilter
{
    // Register a custom routed event using the bubble routing strategy.
    public static readonly RoutedEvent CleanEvent = EventManager.RegisterRoutedEvent(
        "Clean", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));

    // Provide an add handler accessor method for the Clean event.
    public static void AddCleanHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
    {
        if (dependencyObject is not UIElement uiElement)
            return;

        uiElement.AddHandler(CleanEvent, handler);
    }

    // Provide a remove handler accessor method for the Clean event.
    public static void RemoveCleanHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
    {
        if (dependencyObject is not UIElement uiElement)
            return;

        uiElement.RemoveHandler(CleanEvent, handler);
    }
}
Public Class AquariumFilter

    ' Register a custom routed event using the bubble routing strategy.
    Public Shared ReadOnly CleanEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
        "Clean", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(AquariumFilter))

    ' Provide an add handler accessor method for the Clean event.
    Public Shared Sub AddCleanHandler(dependencyObject As DependencyObject, handler As RoutedEventHandler)
        Dim uiElement As UIElement = TryCast(dependencyObject, UIElement)

        If uiElement IsNot Nothing Then
            uiElement.[AddHandler](CleanEvent, handler)
        End If
    End Sub

    ' Provide a remove handler accessor method for the Clean event.
    Public Shared Sub RemoveCleanHandler(dependencyObject As DependencyObject, handler As RoutedEventHandler)
        Dim uiElement As UIElement = TryCast(dependencyObject, UIElement)

        If uiElement IsNot Nothing Then
            uiElement.[RemoveHandler](CleanEvent, handler)
        End If
    End Sub

End Class

添付イベント識別子を返す RegisterRoutedEvent メソッドは、アタッチされていないルーティング イベントの登録に使用されるのと同じメソッドです。 アタッチされたルーティング イベントとアタッチされていないルーティング イベントの両方が、一元化された内部ストアに登録されます。 このイベント ストアの実装により、ルーティング イベントの概要で説明されている "インターフェイスとしてのイベント" の概念 可能になります。

アタッチされていないルーティング イベントをバックするために使用される CLR イベント "ラッパー" とは異なり、アタッチされたイベント アクセサー メソッドは、UIElement または ContentElementから派生しないクラスに実装できます。 これは、アタッチされたイベント バッキング コードが、渡された UIElement インスタンスで UIElement.AddHandler メソッドと UIElement.RemoveHandler メソッドを呼び出すためです。 これに対し、アタッチされていないルーティング イベントの CLR ラッパーは、所有するクラスでこれらのメソッドを直接呼び出すので、クラスは UIElementから派生する必要があります。

WPF 添付イベントを発生させる

アタッチされたイベントを発生させるプロセスは、基本的に、アタッチされていないルーティング イベントの場合と同じです。

通常、これらのイベントは一般的な "サービス" 概念モデルに従っているため、コードで既存の WPF 定義の添付イベントを発生させる必要はありません。 このモデルでは、InputManagerなどのサービス クラスが WPF で定義された添付イベントを発生させる役割を担います。

ルーティング イベントにアタッチされたイベントを基にした WPF モデルを使用してカスタム添付イベントを定義する場合は、UIElement.RaiseEvent メソッドを使用して、任意の UIElement または ContentElementにアタッチされたイベントを発生させます。 ルーティング イベントがアタッチされているかどうかに関係なく、ルーティング イベントを発生させる場合は、要素ツリー内の要素をイベント ソースとして指定する必要があります。 該当のソースは、RaiseEvent 呼び出し元として識別されます。 たとえば、aquarium1での AquariumFilter.Clean アタッチされたルーティングイベントを発生させる場合:

aquarium1.RaiseEvent(new RoutedEventArgs(AquariumFilter.CleanEvent));
aquarium1.[RaiseEvent](New RoutedEventArgs(AquariumFilter.CleanEvent))

前の例では、aquarium1 がイベント ソースです。

関連項目