添付イベントの概要

Extensible Application Markup Language (XAML) は言語コンポーネントと "添付イベント" と呼ばれている種類のイベントを定義します。 添付イベントという概念を利用すると、イベントを実際に定義または継承する要素にではなく、任意の要素に特定のイベントのハンドラーを追加できます。 この場合、イベントを発生させる可能性があるオブジェクトとターゲット処理インスタンスのいずれもイベントを定義せず、"所有" しません。

必須コンポーネント

このトピックは、「ルーティング イベントの概要」と「WPF の XAML」を既に読んでいることを前提としています。

添付イベントの構文

添付イベントには XAML 構文とコーディング パターンが含まれています。コーディング パターンは、添付イベントの使用をサポートする目的でバッキング コードで使用する必要があります。

XAML 構文では、添付イベントはそのイベント名だけでなく、その所有タイプとイベント名により指定されます。所有タイプとイベント名はドット (.) で区切られます。 イベント名はその所有タイプにより修飾されるため、添付イベントの構文では、インスタンス化できるあらゆる要素に添付イベントを添付できます。

たとえば、カスタム NeedsCleaning 添付イベントのハンドラーを添付する XAML 構文は次のようになります。

<aqua:Aquarium Name="theAquarium" Height="600" Width="800" aqua:AquariumFilter.NeedsCleaning="WashMe"/>

aqua: 接頭辞に注意してください。この場合は、マッピングされたカスタム xmlns から来ているカスタム イベントが添付イベントであるため、この接頭辞が必要になります。

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

WPF では、添付イベントは RoutedEvent フィールドによりサポートされ、発生後、ツリー経由でルーティングされます。 通常、添付イベントの発生源 (イベントを発生させたオブジェクト) はシステムまたはサービス ソースです。そのため、イベントを発生させるコードを実行するオブジェクトは要素ツリーの直接的一部ではありません。

添付イベントのシナリオ

WPF では、添付イベントは、サービス レベルの抽象化が存在する特定の機能領域に存在します。たとえば、静的な Mouse クラスまたは Validation クラスにより有効化されたイベントに対するサービス レベルの抽象化です。 サービスと連動する、またはサービスを利用するクラスは、添付イベント構文のイベントを利用するか、クラスによるサービスの機能統合に含まれるルーティング イベントとして添付イベントを添付できます。

WPF は添付イベントの数を定義しますが、添付イベントを直接使用または処理するシナリオは非常に限られています。 一般的には、添付イベントはアーキテクチャ目的にサービスを提供しますが、その後、非添付の (CLR イベント "ラッパー" でサポートされる) ルーティング イベントに転送されます。

たとえば、基になる添付イベント Mouse.MouseDown を特定の UIElement でより簡単に処理するには、添付イベント構文を XAML またはコードで使用するのではなく、その UIElementMouseDown を使用します。 添付イベントは、入力機器の将来の拡張を可能にするため、アーキテクチャにおける目的にサービスを提供します。 想定される機器では、マウスの入力をシミュレートするために、Mouse.MouseDown を発生させることのみが必要となります。その目的で Mouse から派生させる必要はありません。 ただし、このシナリオでは、イベントのコードが処理されます。添付イベントの XAML 処理はこのシナリオには関係しません。

WPF で添付イベントを処理する

添付イベントと記述するハンドラー コードを処理するプロセスは基本的にルーティング イベントの場合と同じです。

一般的には、WPF 添付イベントは WPF ルーティング イベントとそれほど異なっているわけではありません。 違いは、イベントの発生源とメンバーとしてクラスにより公開される方法 (これは XAML ハンドラー構文にも影響を与えます) です。

ただし、前述のように、既存の WPF 添付イベントは WPF での取り扱いを特に意図しているわけではありません。 多くの場合、このイベントの目的は、合成中、合成された要素が親要素に状態を報告できるようにすることです。合成においては、イベントは通常、コードで発生します。また、関連する親クラスにおいて、クラスの処理に依存します。 たとえば、Selector 内の項目は添付 Selected イベントを発生させることが予想されます。このイベントは、Selector クラスにより処理されるクラスであり、可能性として Selector クラスにより別のルーティング イベント SelectionChanged に変換されます。 ルーティング イベントとクラス処理については、「ルーティング イベントの処理済みとしてのマーキング、およびクラス処理」を参照してください。

独自の添付イベントをルーティング イベントとして定義する

共通の WPF 基底クラスから派生させる場合、独自の添付イベントを実装できます。クラスに特定のパターン メソッドを追加し、基底クラスに既に存在するユーティリティ メソッドを使用します。

パターンは次のとおりです。

  • メソッド AddEventNameHandler と 2 つのパラメーター。 最初のパラメーターは、イベント ハンドラーが追加されるインスタンスです。 2 番目のパラメーターは追加するイベント ハンドラーです。 メソッドには publicstatic を指定する必要があります。戻り値はありません。

  • メソッド RemoveEventNameHandler と 2 つのパラメーター。 最初のパラメーターは、イベント ハンドラーが削除されるインスタンスです。 2 番目のパラメーターは削除するイベント ハンドラーです。 メソッドには publicstatic を指定する必要があります。戻り値はありません。

AddEventNameHandler アクセサー メソッドは、添付イベント ハンドラーの属性が要素で宣言されているとき、XAML 処理を容易にします。 AddEventNameHandler メソッドと RemoveEventNameHandler メソッドはまた、添付イベントのイベント ハンドラー ストアへのコード アクセスを可能にします。

この一般的パターンには、フレームワークの実用的実装のための十分な精度がありません。サポートする言語とアーキテクチャで基礎イベントを識別する方法に関して、特定の XAML リーダー実装で異なるスキームが与えられる可能性があるためです。 そのような理由から、WPF は添付イベントをルーティング イベントとして実装します。イベント (RoutedEvent) に使用する識別子が WPF イベント システムによって既に定義されています。 また、イベントのルーティングは、添付イベントの XAML 言語レベル概念で自然な実装です。

WPF 添付イベントの AddEventNameHandler 実装は、ルーティング イベントとハンドラーを引数として AddHandler を呼び出すことで行われます。

この実装方法と一般的なルーティング イベント システムでは、添付イベントの処理が UIElement 派生クラスまたは ContentElement 派生クラスに制限されます。これらのクラスだけに AddHandler 実装が与えられるためです。

たとえば、次のコードでは、添付イベントをルーティング イベントとして宣言する WPF 添付イベント方法を利用し、所有者クラス AquariumNeedsCleaning 添付イベントが定義されます。

public static readonly RoutedEvent NeedsCleaningEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));
public static void AddNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
public static void RemoveNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
Public Shared ReadOnly NeedsCleaningEvent As RoutedEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(AquariumFilter))
Public Shared Sub AddNeedsCleaningHandler(ByVal d As DependencyObject, ByVal handler As RoutedEventHandler)
    Dim uie As UIElement = TryCast(d, UIElement)
    If uie IsNot Nothing Then
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler)
    End If
End Sub
Public Shared Sub RemoveNeedsCleaningHandler(ByVal d As DependencyObject, ByVal handler As RoutedEventHandler)
    Dim uie As UIElement = TryCast(d, UIElement)
    If uie IsNot Nothing Then
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler)
    End If
End Sub

添付イベント識別子フィールド RegisterRoutedEvent を設定するメソッドは、実際、非添付のルーティング イベントを登録するためのメソッドと同じです。 添付イベントとルーティング イベントはすべて、集中管理されている内部ストアに登録されます。 このイベント ストア実装により "インターフェイスとしてのイベント" という概念が可能になります。この概念に関する説明は「ルーティング イベントの概要」にあります。

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

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

ただし、添付イベントの基礎を RoutedEvent とする WPF モデルを基盤にカスタムの添付イベントを定義する場合、RaiseEvent を利用し、任意の UIElement または ContentElement から添付イベントを発生させることができます。 ルーティング イベント (添付または非添付) を発生させるには、イベント ソースとして要素ツリーで特定の要素を宣言する必要があります。そのソースは RaiseEvent 呼び出し元として報告されます。 ツリーのソースとして報告される要素を決定することはサービスの担当です。

関連項目