次の方法で共有


ルーティング イベントの概要

Windows Presentation Foundation (WPF) アプリケーション開発者とコンポーネント作成者は、ルーティング イベントを使用して要素ツリーを介してイベントを伝達し、ツリー内の複数のリスナーでイベント ハンドラーを呼び出すことができます。 これらの機能は、共通言語ランタイム (CLR) イベントでは見つかりません。 いくつかの WPF イベントは、 ButtonBase.Clickなどのルーティング イベントです。 この記事では、ルーティング イベントの基本的な概念について説明し、ルーティング イベントに応答するタイミングと方法に関するガイダンスを提供します。

[前提条件]

この記事では、共通言語ランタイム (CLR)、オブジェクト指向プログラミング、 および WPF 要素レイアウト をツリーとして概念化する方法に関する基本的な知識を前提としています。 この記事の例に従うには、拡張アプリケーション マークアップ言語 (XAML) に慣れている場合や 、WPF アプリケーションを記述する方法を理解している場合に役立ちます。

ルーティング イベントとは?

ルーティング イベントは、機能または実装の観点から検討できます。

  • 機能的な観点から見ると、ルーティング イベントは、イベント ソースだけでなく、要素ツリー内の複数のリスナーでハンドラーを呼び出すことができるイベントの一種です。 イベント リスナーは、イベント ハンドラーがアタッチされて呼び出される要素です。 イベント ソースは、最初にイベントを発生させた要素またはオブジェクトです。

  • 実装の観点から見ると、ルーティング イベントは、WPF イベント システムに登録され、RoutedEvent クラスのインスタンスによってサポートされ、WPF イベント システムによって処理されるイベントです。 通常、ルーティング イベントは CLR イベント "ラッパー" を使用して実装され、CLR イベントと同様に XAML およびコードビハインドでハンドラーをアタッチできます。

WPF アプリケーションには通常、XAML で宣言されたか、コードでインスタンス化された多くの要素が含まれています。 アプリケーションの要素は、その要素ツリー内に存在します。 ルーティング イベントの定義方法に応じて、ソース要素でイベントが発生すると、次のようになります。

  • ソース要素からルート要素 (通常はページまたはウィンドウ) に要素ツリーをバブルアップします。
  • ルート要素からソース要素への要素ツリーをトンネルダウンします。
  • 要素ツリーを通過せず、ソース要素でのみ発生します。

次の部分要素ツリーについて考えてみます。

<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">
    <StackPanel Background="LightBlue" Orientation="Horizontal" Button.Click="YesNoCancelButton_Click">
        <Button Name="YesButton">Yes</Button>
        <Button Name="NoButton">No</Button>
        <Button Name="CancelButton">Cancel</Button>
    </StackPanel>
</Border>

要素ツリーは、次のようにレンダリングされます。

3 つのボタンを含む XAML 要素ツリー:はい、いいえ、キャンセル。

3 つのボタンはそれぞれ、イベント ソース Click 可能性があります。 いずれかのボタンがクリックされると、ボタンからルート要素にバブルアップする Click イベントが発生します。 Button要素とBorder要素にはイベント ハンドラーがアタッチされていませんが、StackPanelはアタッチされます。 ツリーの上位に表示されない他の要素にも、 Click イベント ハンドラーがアタッチされている可能性があります。 Click イベントがStackPanel要素に到達すると、WPF イベント システムは、それにアタッチされているYesNoCancelButton_Click ハンドラーを呼び出します。 この例のClick イベントのイベント ルートは、-Button> -StackPanel> -Border 連続する親要素>です。

最初にルーティング イベントを発生させた要素は、イベント ハンドラー パラメーターの RoutedEventArgs.Source として識別されます。 イベント リスナーは、イベント ハンドラーがアタッチされて呼び出される要素であり、イベント ハンドラー パラメーターの 送信者 として識別されます。

ルーティング イベントの最上位のシナリオ

ルーティング イベントの概念を動機付け、一般的な CLR イベントと区別するシナリオをいくつか次に示します。

  • コントロールの構成とカプセル化: WPF のさまざまなコントロールには、豊富なコンテンツ モデルがあります。 たとえば、 Button内に画像を配置すると、ボタンのビジュアル ツリーが効果的に拡張されます。 ただし、追加されたイメージはボタンのヒット テスト動作を中断してはなりません。これは、ユーザーが画像ピクセルをクリックしたときに応答する必要があります。

  • 単一のハンドラーの添付ポイント: 各ボタンの Click イベントのハンドラーを登録できますが、ルーティング イベントでは、前の XAML の例に示すように 1 つのハンドラーをアタッチできます。 これにより、各ボタンの Click イベントを登録しなくても、ボタンの追加や削除など、単一ハンドラーの下の要素ツリーを変更できます。 Click イベントが発生すると、ハンドラー ロジックはイベントの発生元を特定できます。 前に示した XAML 要素ツリーで指定された次のハンドラーには、そのロジックが含まれています。

    private void YesNoCancelButton_Click(object sender, RoutedEventArgs e)
    {
        FrameworkElement sourceFrameworkElement = e.Source as FrameworkElement;
        switch (sourceFrameworkElement.Name)
        {
            case "YesButton":
                // YesButton logic.
                break;
            case "NoButton":
                // NoButton logic.
                break;
            case "CancelButton":
                // CancelButton logic.
                break;
        }
        e.Handled = true;
    }
    
    Private Sub YesNoCancelButton_Click(sender As Object, e As RoutedEventArgs)
        Dim frameworkElementSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
    
        Select Case frameworkElementSource.Name
            Case "YesButton"
                ' YesButton logic.
            Case "NoButton"
                ' NoButton logic.
            Case "CancelButton"
                ' CancelButton logic.
        End Select
    
        e.Handled = True
    End Sub
    
  • クラス処理: ルーティング イベントは、クラスで定義する クラス イベント ハンドラー をサポートします。 クラス ハンドラーは、クラスの任意のインスタンスで同じイベントのインスタンス ハンドラーの前にイベントを処理します。

  • リフレクションなしでイベントを参照する: 各ルーティング イベントは、 RoutedEvent フィールド識別子を作成して、イベントを識別するために静的または実行時のリフレクションを必要としない堅牢なイベント識別手法を提供します。

ルーティング イベントの実装方法

ルーティング イベントは、WPF イベント システムに登録され、 RoutedEvent クラスのインスタンスによってサポートされ、WPF イベント システムによって処理されるイベントです。 登録から取得した RoutedEvent インスタンスは、通常、 登録したクラスの public static readonly メンバーとして格納されます。 そのクラスはイベントの「オーナー」クラスと呼ばれます。 通常、ルーティング イベントは、同じ名前の CLR イベント "ラッパー" を実装します。 CLR イベント ラッパーには、XAML およびコードビハインドで言語固有のイベント構文を使用してハンドラーをアタッチできるようにするための add アクセサーと remove アクセサーが含まれています。 addアクセサーとremove アクセサーは、CLR 実装をオーバーライドし、ルーティング イベント AddHandlerメソッドとRemoveHandler メソッドを呼び出します。 ルーティング イベントのバッキングと接続のメカニズムは概念的には、依存関係プロパティが、 DependencyProperty クラスによってサポートされ、WPF プロパティ システムに登録される CLR プロパティである方法と似ています。

次の例では、 Tap ルーティング イベントを登録し、返された RoutedEvent インスタンスを格納し、CLR イベント ラッパーを実装します。

// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    name: "Tap",
    routingStrategy: RoutingStrategy.Bubble,
    handlerType: typeof(RoutedEventHandler),
    ownerType: typeof(CustomButton));

// Provide CLR accessors for adding and removing an event handler.
public event RoutedEventHandler Tap
{
    add { AddHandler(TapEvent, value); }
    remove { RemoveHandler(TapEvent, value); }
}
' Register a custom routed event using the Bubble routing strategy.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
    name:="Tap",
    routingStrategy:=RoutingStrategy.Bubble,
    handlerType:=GetType(RoutedEventHandler),
    ownerType:=GetType(CustomButton))

' Provide CLR accessors for adding and removing an event handler.
Public Custom Event Tap As RoutedEventHandler
    AddHandler(value As RoutedEventHandler)
        [AddHandler](TapEvent, value)
    End AddHandler

    RemoveHandler(value As RoutedEventHandler)
        [RemoveHandler](TapEvent, value)
    End RemoveHandler

    RaiseEvent(sender As Object, e As RoutedEventArgs)
        [RaiseEvent](e)
    End RaiseEvent
End Event

ルーティング戦略

ルーティング イベントでは、次の 3 つのルーティング戦略のいずれかを使用します。

  • バブル: 最初に、イベント ソースのイベント ハンドラーが呼び出されます。 そのルーティングイベントは要素ツリーのルートに達するまで、連続する親要素を通りながら、それぞれの親要素のイベントハンドラーを順次呼び出します。 ほとんどのルーティング イベントでは、バブル ルーティング戦略が使用されます。 バブル ルーティング イベントは、通常、複合コントロールまたはその他の UI 要素からの入力または状態の変更を報告するために使用されます。

  • トンネリング: 最初は、要素ツリー ルートのイベント ハンドラーが呼び出されます。 ルーティング イベントは、イベント ソースに到達するまで、順番にイベント ハンドラーを呼び出して、連続する子要素にルーティングします。 トンネリング ルートに続くイベントは、 プレビュー イベントとも呼ばれます。 WPF 入力イベントは、通常、 プレビューとバブルのペアとして実装されます。

  • ダイレクト: イベント ソースのイベント ハンドラーのみが呼び出されます。 この非ルーティング戦略は、標準の CLR イベントである Windows フォーム UI フレームワーク イベントに似ています。 CLR イベントとは異なり、ダイレクト ルーティング イベントは クラス処理 をサポートし、 EventSettersEventTriggers で使用できます。

ルーティング イベントを使用する理由

アプリケーション開発者は、処理しているイベントがルーティング イベントとして実装されていることを常に把握したり注意したりする必要はありません。 ルーティング イベントには特別な動作がありますが、発生させた要素のイベントを処理している場合、その動作はほとんど表示されません。 ただし、ルーティング イベントは、複合コントロール内など、子要素によって発生したイベントを処理するために、親要素にイベント ハンドラーをアタッチする場合に関連します。

ルーティング イベント リスナーは、自分が処理するルーティング イベントがそのクラスのメンバーである必要はありません。 任意の UIElement または ContentElement は、ルーティング イベントのイベント リスナーにすることができます。 ビジュアル要素は UIElement または ContentElementから派生しているため、ルーティング イベントは、アプリケーション内の異なる要素間でのイベント情報の交換をサポートする概念的な "インターフェイス" として使用できます。 ルーティング イベントの "インターフェイス" の概念は、 入力イベントに特に適用できます。

ルーティング イベントは、各リスナーがイベント データの同じインスタンスにアクセスできるため、イベント ルートに沿った要素間でのイベント情報の交換をサポートします。 1 つの要素がイベント データ内の何かを変更した場合、その変更はイベント ルート内の後続の要素に表示されます。

ルーティングの側面とは別に、次のような理由から、標準の CLR イベントではなくルーティング イベントを実装することを選択できます。

  • EventSetters や EventTriggers などの一部の WPF スタイル設定およびテンプレート機能では、参照されるイベントをルーティング イベントにする必要があります。

  • ルーティング イベントは、リスナー クラスの 任意のインスタンスで同じイベントのインスタンス ハンドラーよりも先にイベントを処理するクラス イベント ハンドラーをサポートします。 この機能は、クラス ハンドラーがインスタンス ハンドラーによって誤って抑制できないイベント ドリブン クラスの動作を適用できるため、コントロールの設計に役立ちます。

ルーティング イベント ハンドラーをアタッチして実装する

XAML では、イベント リスナー要素の属性としてイベント名を宣言することで、イベント ハンドラーを要素にアタッチします。 属性値はハンドラー メソッド名です。 ハンドラー メソッドは、XAML ページの分離コード部分クラスに実装する必要があります。 イベント リスナーは、イベント ハンドラーがアタッチされて呼び出される要素です。

リスナー クラスのメンバー (継承またはそれ以外の場合) であるイベントの場合は、次のようにハンドラーをアタッチできます。

<Button Name="Button1" Click="Button_Click">Click me</Button>

イベントがリスナーのクラスのメンバーでない場合は、修飾されたイベント名を <owner type>.<event name>の形式で使用する必要があります。 たとえば、StackPanel クラスはClick イベントを実装していないため、その要素にバブルアップするStackPanel イベントのClickにハンドラーをアタッチするには、修飾イベント名構文を使用する必要があります。

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

分離コード内のイベント ハンドラー メソッドのシグネチャは、ルーティング イベントのデリゲート型と一致する必要があります。 sender イベントのRoutedEventHandler デリゲートの Click パラメーターは、イベント ハンドラーがアタッチされる要素を指定します。 args デリゲートの RoutedEventHandler パラメーターには、イベント データが含まれています。 Button_Click イベント ハンドラーの互換性のある分離コード実装は、次のようになります。

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Click event logic.
}
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Click event logic.
End Sub

RoutedEventHandlerは基本的なルーティング イベント ハンドラー デリゲートですが、一部のコントロールまたは実装シナリオでは、より特殊なイベント データをサポートする異なるデリゲートが必要です。 たとえば、 DragEnter ルーティング イベントの場合、ハンドラーは DragEventHandler デリゲートを実装する必要があります。 これにより、ハンドラー コードは、ドラッグ操作のクリップボード ペイロードを含むイベント データの DragEventArgs.Data プロパティにアクセスできます。

ルーティング イベント ハンドラーを追加するための XAML 構文は、標準の CLR イベント ハンドラーの場合と同じです。 XAML でのイベント ハンドラーの追加の詳細については、「WPF での XAML」を参照してください。 XAML を使用して要素にイベント ハンドラーをアタッチする方法の完全な例については、「ルーティング イベントを処理する方法」を参照してください。

コードを使用してルーティング イベントのイベント ハンドラーを要素にアタッチするには、通常、次の 2 つのオプションがあります。

  • AddHandler メソッドを直接呼び出します。 ルーティング イベント ハンドラーは、常にこの方法でアタッチできます。 次の例では、Click メソッドを使用して、AddHandler イベント ハンドラーをボタンにアタッチします。

    Button1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    Button1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    

    ボタンのClick イベントのハンドラーをイベントのルート内の別の要素 (StackPanel という名前のStackPanel1など) にアタッチするには:

    StackPanel1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    StackPanel1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    
  • ルーティング イベントで CLR イベント ラッパーが実装されている場合は、言語固有のイベント構文を使用して、標準の CLR イベントの場合と同様にイベント ハンドラーを追加します。 ほとんどの既存の WPF ルーティング イベントは CLR ラッパーを実装するため、言語固有のイベント構文が有効になります。 次の例では、言語固有の構文を使用して、 Click イベント ハンドラーをボタンにアタッチします。

    Button1.Click += Button_Click;
    
    AddHandler Button1.Click, AddressOf Button_Click
    

コードでイベント ハンドラーをアタッチする方法の例については、「コードを 使用してイベント ハンドラーを追加する方法」を参照してください。 Visual Basic でコーディングする場合は、 Handles キーワードを使用してハンドラー宣言の一部としてハンドラーを追加することもできます。 詳細については、「 Visual Basic と WPF のイベント処理」を参照してください。

処理の概念

すべてのルーティング イベントは、 RoutedEventArgs クラスであるイベント データの共通基本クラスを共有します。 RoutedEventArgs クラスは、ブールHandledプロパティを定義します。 Handled プロパティの目的は、イベント ルートに沿ってイベント ハンドラーがルーティング イベントを処理済みとしてマークできるようにすることです。 イベントを処理済みとしてマークするには、 Handled の値をイベント ハンドラー コードで true に設定します。

Handledの値は、イベント ルートに沿って移動するルーティング イベントの処理方法に影響します。 ルーティング イベントの共有イベント データに Handledtrue されている場合、イベント ルートに沿って他の要素にアタッチされたハンドラーは、通常、その特定のイベント インスタンスに対して呼び出されません。 ほとんどの一般的なハンドラー シナリオでは、イベントを処理対象としてマークすると、イベント ルートに沿った後続のハンドラー (インスタンスまたはクラス ハンドラー) がその特定のイベント インスタンスに応答するのを効果的に停止します。 ただし、まれに、処理済みとしてマークされているルーティング イベントに応答するためにイベント ハンドラーが必要な場合は、次のことができます。

Handledの概念は、アプリケーションの設計方法とイベント ハンドラーのコーディング方法に影響する可能性があります。 ルーティング イベントを処理するための単純なプロトコルとして Handled を概念化できます。 このプロトコルの使用方法はユーザー次第ですが、 Handled パラメーターの想定される使用方法は次のとおりです。

  • ルーティング イベントが処理済みとしてマークされている場合、ルートに沿って他の要素によって再処理される必要はありません。

  • ルーティング イベントが処理済みとしてマークされていない場合、イベント ルートの前のリスナーにはイベントのハンドラーが存在しないか、登録されているハンドラーがイベントに応答せず、イベントを処理済みとしてマークすることを正当化します。 現在のリスナーのハンドラーには、次の 3 つの可能なアクション コースがあります。

    • まったくアクションを実行しません。 イベントは未処理のままであり、ツリー内の次のリスナーにルーティングされます。

    • イベントに応答してコードを実行しますが、イベントを処理済みとしてマークすることを正当化する程度には実行しません。 イベントは未処理のままであり、ツリー内の次のリスナーにルーティングされます。

    • イベントを処理済みとしてマークすることを正当化する範囲で、イベントに応答してコードを実行します。 イベント データで処理済みとしてイベントをマークします。 イベントは引き続きツリー内の次のリスナーにルーティングされますが、ほとんどのリスナーはそれ以上ハンドラーを呼び出しません。 例外は、 handledEventsTootrue に設定されたハンドラーを持つリスナーです。

ルーティング イベントの処理の詳細については、「ルーティング イベントを 処理済みとしてマークする」および「クラスの処理」を参照してください。

発生したオブジェクトのバブル ルーティング イベントのみを処理する開発者は、他のリスナーを気にしない可能性がありますが、とにかくイベントを処理済みとしてマークすることをお勧めします。 これにより、イベント ルートに沿った要素に同じルーティング イベントのハンドラーがある場合、予期しない副作用が回避されます。

クラスハンドラ

ルーティング イベント ハンドラーには、 インスタンス ハンドラーまたは クラス ハンドラーのいずれかを指定できます。 特定のクラスのクラス ハンドラーは、そのクラスの任意のインスタンスで同じイベントに応答するインスタンス ハンドラーの前に呼び出されます。 この動作により、ルーティング イベントが処理済みとしてマークされると、多くの場合、クラス ハンドラー内でそのようにマークされます。 クラス ハンドラーには、次の 2 種類があります。

一部の WPF コントロールには、特定のルーティング イベントに固有のクラス処理があります。 クラス処理により、ルーティング イベントがまるで一度も発生していないように見えることがありますが、実際にはクラス ハンドラーによって処理済みとしてマークされています。 イベント ハンドラーが処理されたイベントに応答する必要がある場合、ハンドラーをhandledEventsTootrueに設定して登録できます。 詳細については、独自のクラス ハンドラーの実装と望ましくないクラス処理の回避の両方について詳しくは、「 ルーティング イベントを処理済みとしてマークする」および「クラス処理」を参照してください。

WPF の添付イベント

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

XAML 構文では、添付イベントは、そのイベント名 所有者の型によって、 <owner type>.<event name>の形式で指定されます。 イベント名は所有者型の名前で 修飾 されているため、構文により、インスタンス化できる任意の要素にイベントをアタッチできます。 この構文は、イベント ルートに沿って任意の要素にアタッチする通常のルーティング イベントのハンドラーにも適用できます。 ハンドラーをアタッチする必要があるオブジェクトに対して AddHandler メソッドを呼び出すことによって、分離コード内の添付イベントのハンドラーをアタッチすることもできます。

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

WPF の添付イベントの詳細については、「 添付イベントの概要」を参照してください。

XAML での修飾イベント名

<owner type>.<event name>構文は、イベント名とその所有者の種類の名前を修飾します。 この構文を使用すると、イベントをクラスのメンバーとして実装する要素だけでなく、任意の要素にイベントをアタッチできます。 構文は、アタッチ されたイベントまたはイベント ルートに沿った任意の要素のルーティング イベントに対して XAML でハンドラーをアタッチする場合に適用されます。 子要素で発生したルーティング イベントを処理するために、ハンドラーを親要素にアタッチするシナリオを考えてみましょう。 親要素にルーティング イベントがメンバーとして含まれていない場合は、修飾イベント名の構文を使用する必要があります。 例えば次が挙げられます。

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

この例では、イベント ハンドラーが追加される親要素リスナーは StackPanelです。 ただし、 Click ルーティング イベントは、 ButtonBase クラスで実装および発生し、継承によって Button クラスで使用できます。 Button クラスはClick イベントを "所有" しますが、ルーティング イベント システムは、ルーティング イベントのハンドラーを、CLR イベントのハンドラーを持つ可能性がある任意のUIElementまたはContentElementインスタンス リスナーにアタッチすることを許可します。 これらの修飾されたイベント属性名の既定の xmlns 名前空間は、通常、既定の WPF xmlns 名前空間ですが、カスタム ルーティング イベントのプレフィックス付き名前空間を指定することもできます。 xmlnsの詳細については、「WPF XAML の XAML 名前空間と名前空間マッピング」を参照してください。

WPF 入力イベント

WPF プラットフォーム内のルーティング イベントの 1 つの頻繁なアプリケーションは、 入力イベント用です

ペアになっている WPF 入力イベントは、マウス ボタンの押下などの入力デバイスからの単一のユーザー アクションによって、プレビューイベントとバブル ルーティング イベントが順番に発生するように実装されます。 まず、プレビュー イベントが発生し、そのルートが完了します。 プレビュー イベントが完了すると、バブル イベントが発生し、そのルートが完了します。 バブル イベントを発生させる実装クラスの RaiseEvent メソッド呼び出しでは、バブル イベントのプレビュー イベントのイベント データが再利用されます。

処理済みとしてマークされたプレビュー入力イベントは、プレビュー ルートの残りの部分に対して通常登録されているイベント ハンドラーを呼び出しません。ペアのバブル イベントは発生しません。 この処理動作は、ヒット テスト ベースの入力イベントまたはフォーカスベースの入力イベントをコントロールの最上位レベルで報告する複合コントロール デザイナーに役立ちます。 コントロールの最上位要素には、コントロール サブコンポーネントからのプレビュー イベントをクラス処理して、最上位のコントロール固有のイベントに "置き換える" 機会があります。

入力イベント処理のしくみを説明するために、次の入力イベントの例を考えてみましょう。 次のツリー図では、 leaf element #2 は、 PreviewMouseDown イベントと MouseDown ペアイベントの両方のソースです。

ルート要素から他の要素にイベント ルーティングがどのように流れるかを示す図。

リーフ要素 #2 でのマウスダウン アクションの後のイベント処理の順序は次のとおりです。

  1. PreviewMouseDown ルート要素のトンネリング イベント。
  2. PreviewMouseDown 中間要素 #1 のトンネリング イベント。
  3. PreviewMouseDown リーフ要素 #2 のトンネリング イベント。これはソース要素です。
  4. MouseDown リーフ要素 #2 (ソース要素) のバブル イベント。
  5. MouseDown 中間要素 #1 のバブル イベント。
  6. MouseDown ルート要素のバブル イベント。

ルーティング イベント ハンドラー デリゲートは、イベントを発生させたオブジェクトとハンドラーが呼び出されたオブジェクトの両方への参照を提供します。 最初にイベントを発生させたオブジェクトは、イベント データの Source プロパティによって報告されます。 ハンドラーが呼び出されたオブジェクトは、 sender パラメーターによって報告されます。 特定のルーティング イベント インスタンスの場合、イベントを発生させたオブジェクトは、イベントが要素ツリーを通過しても変更されませんが、 sender は変更されます。 前の図の手順 3 と 4 では、 Sourcesender は同じオブジェクトです。

入力イベント ハンドラーが、イベントに対処するために必要なアプリケーション固有のロジックを完了する場合は、入力イベントを処理済みとしてマークする必要があります。 通常、入力イベントが Handledマークされると、イベント ルートに沿ったハンドラーは呼び出されません。 ただし、handledEventsTooに設定されたtrue パラメーターに登録されている入力イベント ハンドラーは、イベントが処理済みとしてマークされている場合でも呼び出されます。 詳細については、「 プレビュー イベント 」および 「ルーティング イベントを処理済みとしてマークする」および「クラスの処理」を参照してください。

プレビュー イベントとバブル イベント のペアの概念。共有イベント データとプレビュー イベントの順次発生、その後のバブル イベントは、一部の WPF 入力イベントにのみ適用され、すべてのルーティング イベントには適用されません。 高度なシナリオに対処するために独自の入力イベントを実装する場合は、WPF 入力イベント ペアのアプローチに従することを検討してください。

入力イベントに応答する独自の複合コントロールを実装する場合は、プレビュー イベントを使用して、サブコンポーネントで発生した入力イベントを抑制し、完全なコントロールを表す最上位のイベントに置き換えることを検討してください。 詳細については、「 ルーティング イベントを処理済みとしてマークする」および「クラス処理」を参照してください。

WPF 入力システムと、一般的なアプリケーション シナリオでの入力とイベントの相互作用の詳細については、「入力の概要」を参照してください。

EventSetters と EventTriggers

マークアップ スタイルでは、 EventSetterを使用して、事前に宣言された XAML イベント処理構文を含めることができます。 XAML が処理されると、参照されるハンドラーがスタイル設定されたインスタンスに追加されます。 ルーティング イベントの EventSetter のみを宣言できます。 次の例では、参照される ApplyButtonStyle イベント ハンドラー メソッドが分離コードに実装されています。

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type Button}">
            <EventSetter Event="Click" Handler="ApplyButtonStyle"/>
        </Style>
    </StackPanel.Resources>
    <Button>Click me</Button>
    <Button Click="Button_Click">Click me</Button>
</StackPanel>

Style ノードには、指定した型のコントロールに関連する他のスタイル情報が既に含まれている可能性があり、EventSetterをこれらのスタイルの一部に設定すると、マークアップ レベルでもコードの再利用が促進されます。 また、 EventSetter は、一般的なアプリケーションおよびページ マークアップから離れたハンドラーのメソッド名を抽象化します。

WPF のルーティング イベントとアニメーション機能を組み合わせた別の特殊な構文は、 EventTriggerです。 EventSetterと同様に、ルーティング イベントのEventTriggerのみを宣言できます。 通常、 EventTrigger はスタイルの一部として宣言されますが、 EventTrigger は、 Triggers コレクションの一部として、または ControlTemplateでページ レベルの要素で宣言できます。 EventTriggerを使用すると、ルーティング イベントがそのイベントのStoryboardを宣言するルート内の要素に到達するたびに実行されるEventTriggerを指定できます。 イベントを処理するだけで既存のストーリーボードを開始するよりも EventTrigger の利点は、 EventTrigger がストーリーボードとその実行時の動作をより適切に制御できる点です。 詳細については、開始後にイベント トリガーを使用してストーリーボードを制御する方法に関するページを参照してください。

ルーティング イベントの詳細

独自のクラスでカスタム ルーティング イベントを作成する際の出発点として、この記事の概念とガイダンスを使用できます。 特殊なイベント データ クラスとデリゲートを使用して、カスタム イベントをサポートすることもできます。 ルーティング イベントの所有者には任意のクラスを指定できますが、有効にするには、ルーティング イベントを UIElement または ContentElement 派生クラスによって発生させ、処理する必要があります。 カスタム イベントの詳細については、「 カスタム ルーティング イベントの作成」を参照してください。

こちらも参照ください