添付イベントの概要 (WPF .NET)
拡張アプリケーション マークアップ言語 (XAML) は、添付イベントと呼ばれる言語コンポーネントとイベントの種類を定義します。 アタッチされたイベントを使用して、非要素クラスで新しい ルーティング イベント を定義し、ツリー内の任意の要素でそのイベントを発生させることができます。 これを行うには、添付イベントをルーティング イベントとして登録し、アタッチされたイベント機能をサポートする特定の バッキング コード を提供する必要があります。 添付イベントはルーティング イベントとして登録されるため、要素で発生すると、要素ツリーを通じて伝達されます。
前提 条件
この記事では、Windows Presentation Foundation (WPF) ルーティング イベントに関する基本的な知識を前提としており、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 処理は関連しないため、このシナリオにはイベントのコード処理が含まれます。
添付イベントを処理する
アタッチされたイベントをコーディングして処理するプロセスは、基本的に、アタッチされていないルーティング イベントの場合と同じです。
既に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
がイベント ソースです。
関連項目
- ルーティング イベントの概要
- XAML 構文の詳細
- WPFのXAMLおよびカスタムクラスに関する
.NET Desktop feedback