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

このトピックでは、Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) でのルーティング イベントの概念について説明します。This topic describes the concept of routed events in Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF). ここでは、ルーティング イベントの用語を定義し、要素ツリーを通じたルーティング イベントのルーティング方法、ルーティング イベントの処理方法、カスタム ルーティング イベントの作成方法について説明します。The topic defines routed events terminology, describes how routed events are routed through a tree of elements, summarizes how you handle routed events, and introduces how to create your own custom routed events.


ここでの説明は、共通言語ランタイム (CLR)、オブジェクト指向プログラミング、ツリー形式として概念化できる WPFWPF 要素間のリレーションシップに関する基礎知識を前提にしています。This topic assumes that you have basic knowledge of the common language runtime (CLR) and object-oriented programming, as well as the concept of how the relationships between WPFWPF elements can be conceptualized as a tree. このトピックの例に従うには、Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML) について理解し、ごく基本的な WPFWPF アプリケーションまたはページを作成できる必要があります。In order to follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML) and know how to write very basic WPFWPF applications or pages. 詳細については、「チュートリアル:初めての WPF デスクトップ アプリケーション」と「XAML の概要 (WPF)」を参照してください。For more information, see Walkthrough: My first WPF desktop application and XAML Overview (WPF).

ルーティング イベントとはWhat Is a Routed Event?

ルーティング イベントについては、機能または実装の観点から考えることができます。You can think about routed events either from a functional or implementation perspective. どちらを便利と感じるかは個人によって異なるため、ここでは両方の見方を提示します。Both definitions are presented here, because some people find one or the other definition more useful.

機能定義: ルーティング イベントは、イベントを生成したオブジェクト上だけでなく、要素ツリー内の複数のリスナー上でハンドラーを呼び出すことができる種類のイベントです。Functional definition: A routed event is a type of event that can invoke handlers on multiple listeners in an element tree, rather than just on the object that raised the event.

実装定義: ルーティング イベントは、RoutedEvent クラスのインスタンスによってバッキングされ、Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) イベント システムによって処理される CLR イベントです。Implementation definition: A routed event is a CLR event that is backed by an instance of the RoutedEvent class and is processed by the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) event system.

一般的な WPFWPF アプリケーションには、多数の要素が含まれます。A typical WPFWPF application contains many elements. コードで作成したか XAMLXAML の宣言によって作成したかにかかわらず、これらの要素は互いに要素ツリー リレーションシップにあります。Whether created in code or declared in XAMLXAML, these elements exist in an element tree relationship to each other. イベントのルーティングは、イベント定義に従って 2 つの方向のいずれかをたどることができますが、一般にはルーティングは発生元要素から要素ツリーに沿って "浮上" し、最終的には要素ツリーのルート (通常はページまたはウィンドウ) に到達します。The event route can travel in one of two directions depending on the event definition, but generally the route travels from the source element and then "bubbles" upward through the element tree until it reaches the element tree root (typically a page or a window). このバブル (浮上) の概念は、DHTML オブジェクト モデルで使用される概念と似ています。This bubbling concept might be familiar to you if you have worked with the DHTML object model previously.

たとえば、次のような単純な要素ツリーがあるとします。Consider the following simple element tree:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>

この要素ツリーでは、次のようなものが生成されます。This element tree produces something like the following:

[はい]、[いいえ]、[キャンセル] ボタンYes, No, and Cancel buttons

この簡略化された要素ツリーでは、Click イベントのソースは Button 要素の 1 つであり、いずれの Button がクリックされた場合でも、イベントを処理する機会が与えられる最初の要素になります。In this simplified element tree, the source of a Click event is one of the Button elements, and whichever Button was clicked is the first element that has the opportunity to handle the event. ただし、Button にアタッチされたハンドラーがイベントに対して動作しない場合、イベントは要素ツリーの Button 親 (StackPanel) までバブルされます。But if no handler attached to the Button acts on the event, then the event will bubble upwards to the Button parent in the element tree, which is the StackPanel. イベントは Border までバブルされた後、要素ツリーのページ ルート (図示されていません) までバブルされる可能性があります。Potentially, the event bubbles to Border, and then beyond to the page root of the element tree (not shown).

つまり、この Click イベントのイベント ルートは次のようになります。In other words, the event route for this Click event is:


ルーティング イベントのトップレベルのシナリオTop-level Scenarios for Routed Events

ルーティング イベントの概念が生まれる契機となったシナリオと、このシナリオに通常の CLR イベントが適していない理由について簡単に説明します。The following is a brief summary of the scenarios that motivated the routed event concept, and why a typical CLR event was not adequate for these scenarios:

コントロールの合成とカプセル化: WPFWPF のさまざまなコントロールには、リッチ コンテンツ モデルがあります。Control composition and encapsulation: Various controls in WPFWPF have a rich content model. たとえば、Button の内部に画像を配置すると、ボタンのビジュアル ツリーを効果的に拡張できます。For example, you can place an image inside of a Button, which effectively extends the visual tree of the button. ただし、厳密には画像の一部であるピクセルをユーザーがクリックした場合でも、この追加した画像によって、ボタンがコンテンツの Click に応答するヒットテスト動作が中断されないようにする必要があります。However, the added image must not break the hit-testing behavior that causes a button to respond to a Click of its content, even if the user clicks on pixels that are technically part of the image.

単一ハンドラーのアタッチ ポイント: Windows フォームでは、複数の要素から発生する可能性のあるイベントを処理するために、同じハンドラーを複数回アタッチする必要があります。Singular handler attachment points: In Windows Forms, you would have to attach the same handler multiple times to process events that could be raised from multiple elements. ルーティング イベントを使用すると、前の例に示したとおり、ハンドラーを一度だけアタッチし、必要に応じてハンドラーのロジックを使用してイベントの発生元を特定することができます。Routed events enable you to attach that handler only once, as was shown in the previous example, and use handler logic to determine where the event came from if necessary. たとえば、前に示した XAMLXAML では次のようなハンドラーを使用します。For instance, this might be the handler for the previously shown XAMLXAML:

private void CommonClickHandler(object sender, RoutedEventArgs e)
  FrameworkElement feSource = e.Source as FrameworkElement;
  switch (feSource.Name)
    case "YesButton":
      // do something here ...
    case "NoButton":
      // do something ...
    case "CancelButton":
      // do something ...
Private Sub CommonClickHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
  Dim feSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
  Select Case feSource.Name
    Case "YesButton"
      ' do something here ...
    Case "NoButton"
      ' do something ...
    Case "CancelButton"
      ' do something ...
  End Select
End Sub

クラス処理: ルーティング イベントでは、クラスに定義した静的ハンドラーを使用できます。Class handling: Routed events permit a static handler that is defined by the class. このクラス ハンドラーでは、アタッチされたどのインスタンス ハンドラーよりも先にイベントを処理できます。This class handler has the opportunity to handle an event before any attached instance handlers can.

リフレクションを使用しないイベントの参照: 一部のコードとマークアップ手法には、特定のイベントを識別する方法が必要です。Referencing an event without reflection: Certain code and markup techniques require a way to identify a specific event. ルーティング イベントによって識別子として作成される RoutedEvent フィールドは、信頼性の高いイベント識別テクニックとして利用でき、静的または実行時のリフレクションを必要としません。A routed event creates a RoutedEvent field as an identifier, which provides a robust event identification technique that does not require static or run-time reflection.

ルーティング イベントの実装方法How Routed Events Are Implemented

ルーティング イベントは、RoutedEvent クラスのインスタンスによってバッキングされ、WPFWPF イベント システムに登録される CLR イベントです。A routed event is a CLR event that is backed by an instance of the RoutedEvent class and registered with the WPFWPF event system. 登録から取得された RoutedEvent インスタンスは、通常、ルーティング イベントを登録し、したがって "所有" しているクラスの public static readonly フィールド メンバーとして保持されます。The RoutedEvent instance obtained from registration is typically retained as a public static readonly field member of the class that registers and thus "owns" the routed event. 一意の名前を持つ CLR イベント ("ラッパー" イベントとも呼ばれます) への接続は、CLR イベントの add 実装と remove 実装をオーバーライドすることにより得られます。The connection to the identically named CLR event (which is sometimes termed the "wrapper" event) is accomplished by overriding the add and remove implementations for the CLR event. 通常、addremove は、暗黙の既定のままにされ、そのイベントのハンドラーの追加や削除に言語固有の適切なイベント構文が使用されます。Ordinarily, the add and remove are left as an implicit default that uses the appropriate language-specific event syntax for adding and removing handlers of that event. ルーティング イベントのバッキングおよび接続メカニズムは、依存関係プロパティが、DependencyProperty クラスによってバッキングされ、WPFWPF プロパティ システムに登録される CLR プロパティであるという概念に似ています。The routed event backing and connection mechanism is conceptually similar to how a dependency property is a CLR property that is backed by the DependencyProperty class and registered with the WPFWPF property system.

RoutedEvent 識別子フィールドと Tap CLR イベントの add 実装および remove 実装の登録と公開を含む、カスタムの Tap ルーティング イベントの宣言を次の例に示します。The following example shows the declaration for a custom Tap routed event, including the registration and exposure of the RoutedEvent identifier field and the add and remove implementations for the Tap CLR event.

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

// Provide CLR accessors for the event
public event RoutedEventHandler Tap
        add { AddHandler(TapEvent, value); }
        remove { RemoveHandler(TapEvent, value); }
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(MyButtonSimple))

' Provide CLR accessors for the event
Public Custom Event Tap As RoutedEventHandler
    AddHandler(ByVal value As RoutedEventHandler)
        Me.AddHandler(TapEvent, value)
    End AddHandler

    RemoveHandler(ByVal value As RoutedEventHandler)
        Me.RemoveHandler(TapEvent, value)
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
    End RaiseEvent
End Event

ルーティング イベント ハンドラーと XAMLRouted Event Handlers and XAML

XAMLXAML を使用してイベントのハンドラーを追加するには、イベント リスナーである要素の属性としてイベント名を宣言します。To add a handler for an event using XAMLXAML, you declare the event name as an attribute on the element that is an event listener. 属性の値は、実装したハンドラー メソッドの名前です。このメソッドは、分離コード ファイルの部分クラス内に存在する必要があります。The value of the attribute is the name of your implemented handler method, which must exist in the partial class of the code-behind file.

<Button Click="b1SetColor">button</Button>

標準の CLR イベント ハンドラーを追加する XAMLXAML 構文は、ルーティング イベント ハンドラーを追加する構文と同じです。これは、実際にはハンドラーを CLR イベント ラッパーに追加しているためです。このラッパーの内部にルーティング イベント実装が存在します。The XAMLXAML syntax for adding standard CLR event handlers is the same for adding routed event handlers, because you are really adding handlers to the CLR event wrapper, which has a routed event implementation underneath. イベント ハンドラーをXAMLXAML で追加する方法の詳細については、「XAML の概要 (WPF)」を参照してください。For more information about adding event handlers in XAMLXAML, see XAML Overview (WPF).

ルーティング方法Routing Strategies

ルーティング イベントは、3 つのルーティング方法のいずれかを使用します。Routed events use one of three routing strategies:

  • バブリング: イベント ソースに対してイベント ハンドラーが呼び出されます。Bubbling: Event handlers on the event source are invoked. ルーティング イベントは、次に、要素ツリー ルートに到達するまで、連続する親要素にルーティングします。The routed event then routes to successive parent elements until reaching the element tree root. ほとんどのルーティング イベントでは、このバブル ルーティング方法を使用します。Most routed events use the bubbling routing strategy. バブル ルーティング イベントは、一般に個別のコントロールまたはその他の UI 要素からの入力や状態変化を報告するために使用されます。Bubbling routed events are generally used to report input or state changes from distinct controls or other UI elements.

  • 直接: ソース要素自体のみに、応答としてハンドラーを呼び出す機会が与えられます。Direct: Only the source element itself is given the opportunity to invoke handlers in response. これは、Windows フォームでイベントに使用されている "ルーティング" に似ています。This is analogous to the "routing" that Windows Forms uses for events. ただし、標準の CLR イベントとは異なり、直接ルーティング イベントはクラス処理をサポートし (クラス処理については次のセクションで説明します)、EventSetter および EventTrigger で使用できます。However, unlike a standard CLR event, direct routed events support class handling (class handling is explained in an upcoming section) and can be used by EventSetter and EventTrigger.

  • トンネリング: 要素ツリー ルートのイベント ハンドラーが最初に呼び出されます。Tunneling: Initially, event handlers at the element tree root are invoked. ルーティング イベントは、次に、経路沿いにルーティング イベント ソース (ルーティング イベントを発生させた要素) のノード要素まで、連続する子要素間の経路をたどります。The routed event then travels a route through successive child elements along the route, towards the node element that is the routed event source (the element that raised the routed event). 多くの場合にトンネル ルーティング イベントは、コントロールの複合部分として使用または処理されます。たとえば、複合部分で発生したイベントは、完全なコントロールに固有のイベントによって意図的に抑止されるか置き換えられます。Tunneling routed events are often used or handled as part of the compositing for a control, such that events from composite parts can be deliberately suppressed or replaced by events that are specific to the complete control. 多くの場合、WPFWPF から提供される入力イベントはトンネルとバブルのペアとして実装されます。Input events provided in WPFWPF often come implemented as a tunneling/bubbling pair. トンネル イベントは、このペアに使用される名前付け規則から、プレビュー イベントと呼ばれることもあります。Tunneling events are also sometimes referred to as Preview events, because of a naming convention that is used for the pairs.

ルーティング イベントを使用する理由Why Use Routed Events?

アプリケーションを開発するときに、処理するイベントがルーティング イベントとして実装されているかどうかを常に確認する必要はありません。As an application developer, you do not always need to know or care that the event you are handling is implemented as a routed event. ルーティング イベントの動作は独特ですが、イベントを発生元の要素で処理する限り、動作はあまり表面には見えません。Routed events have special behavior, but that behavior is largely invisible if you are handling an event on the element where it is raised.

ルーティング イベントが効果を発揮するのは、共通ハンドラーを共通ルートに定義する、独自のコントロールを複合化する、カスタム コントロール クラスを定義するなどの、特定のシナリオの場合です。Where routed events become powerful is if you use any of the suggested scenarios: defining common handlers at a common root, compositing your own control, or defining your own custom control class.

ルーティング イベント リスナーとルーティング イベント ソースは、階層内で共通イベントを共有する必要はありません。Routed event listeners and routed event sources do not need to share a common event in their hierarchy. UIElement または ContentElement は、どのルーティング イベントのイベント リスナーであってもかまいません。Any UIElement or ContentElement can be an event listener for any routed event. したがって、ワーキング API セットで使用可能なすべてのルーティング イベントを概念的 "インターフェイス" として使用できます。これにより、アプリケーション内の異なる要素間でイベント情報を交換できます。Therefore, you can use the full set of routed events available throughout the working API set as a conceptual "interface" whereby disparate elements in the application can exchange event information. ルーティング イベントのこの "インターフェイス" 概念は、特に入力イベントに当てはまります。This "interface" concept for routed events is particularly applicable for input events.

また、ルーティング イベントは、要素ツリー間の通信にも使用できます。これは、イベントのイベント データが経路上の各要素に永続的に保持されるためです。Routed events can also be used to communicate through the element tree, because the event data for the event is perpetuated to each element in the route. ある要素でイベント データの一部を変更した場合、この変更は経路上の次の要素で使用できます。One element could change something in the event data, and that change would be available to the next element in the route.

ルーティングの面以外でも、特定の WPFWPF イベントを標準の CLR イベントではなくルーティング イベントとして実装する理由が他に 2 つあります。Other than the routing aspect, there are two other reasons that any given WPFWPF event might be implemented as a routed event instead of a standard CLR event. 独自のイベントを実装している場合は、次の原則も考慮します。If you are implementing your own events, you might also consider these principles:

  • EventSetterEventTrigger などの特定の WPFWPF スタイル指定機能およびテンプレート機能では、参照先のイベントがルーティング イベントである必要があります。Certain WPFWPF styling and templating features such as EventSetter and EventTrigger require the referenced event to be a routed event. これは、前に説明したイベント識別子シナリオです。This is the event identifier scenario mentioned earlier.

  • ルーティング イベントは、クラス処理機構をサポートしています。この機構により、クラスに静的メソッドを指定して、登録されたインスタンス ハンドラーがルーティング イベントにアクセスする前にこの静的メソッドでイベントを処理することができます。Routed events support a class handling mechanism whereby the class can specify static methods that have the opportunity to handle routed events before any registered instance handlers can access them. インスタンスでのイベント処理によって誤って抑止されずにイベント駆動のクラス動作を適用できるため、これはコントロールをデザインするときに便利です。This is very useful in control design, because your class can enforce event-driven class behaviors that cannot be accidentally suppressed by handling an event on an instance.

前に示した一連の考慮事項については、このトピックのセクションで個別に説明します。Each of the above considerations is discussed in a separate section of this topic.

ルーティング イベントのイベント ハンドラーの追加と実装Adding and Implementing an Event Handler for a Routed Event

XAMLXAML でイベント ハンドラーを追加するには、次の例に示すように、単にイベント名を要素に属性として追加し、属性の値として、適切なデリゲートを実装するイベント ハンドラーの名前を設定します。To add an event handler in XAMLXAML, you simply add the event name to an element as an attribute and set the attribute value as the name of the event handler that implements an appropriate delegate, as in the following example.

<Button Click="b1SetColor">button</Button>

b1SetColor は、Click イベントを処理するコードが含まれる実装済みハンドラーの名前です。b1SetColor is the name of the implemented handler that contains the code that handles the Click event. b1SetColor は、RoutedEventHandler デリゲート、つまり Click イベント用のイベント ハンドラー デリゲートと同じシグネチャを持つ必要があります。b1SetColor must have the same signature as the RoutedEventHandler delegate, which is the event handler delegate for the Click event. すべてのルーティング イベント ハンドラー デリゲートの 1 番目のパラメーターでは、イベント ハンドラーの追加先の要素を指定し、2 番目のパラメーターでは、イベントのデータを指定します。The first parameter of all routed event handler delegates specifies the element to which the event handler is added, and the second parameter specifies the data for the event.

void b1SetColor(object sender, RoutedEventArgs args)
  //logic to handle the Click event
Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
  'logic to handle the Click event
End Sub

RoutedEventHandler は、基本的なルーティング イベント ハンドラー デリゲートです。RoutedEventHandler is the basic routed event handler delegate. 特定のコントロールやシナリオに特化したルーティング イベントの場合、ルーティング イベント ハンドラーに使用するデリゲートも、より特化したものとなることがあるため、特別なイベント データを転送できます。For routed events that are specialized for certain controls or scenarios, the delegates to use for the routed event handlers also might become more specialized, so that they can transmit specialized event data. たとえば、一般的な入力のシナリオで、DragEnter ルーティング イベントを処理することがあります。For instance, in a common input scenario, you might handle a DragEnter routed event. ハンドラーでは、DragEventHandler デリゲートを実装する必要があります。Your handler should implement the DragEventHandler delegate. 最も限定的なデリゲートを使用することにより、DragEventArgs をハンドラーで処理して、ドラッグ操作のクリップボード ペイロードが含まれる Data プロパティを読み取ることができます。By using the most specific delegate, you can process the DragEventArgs in the handler and read the Data property, which contains the clipboard payload of the drag operation.

XAMLXAML を使用してイベント ハンドラーを要素に追加する方法の詳細な例については、「ルーティング イベントを処理する」を参照してください。For a complete example of how to add an event handler to an element using XAMLXAML, see Handle a Routed Event.

コードで作成されたアプリケーションでルーティング イベントのハンドラーを追加するのは簡単です。Adding a handler for a routed event in an application that is created in code is straightforward. ルーティング イベントのハンドラーは、いつでもヘルパー メソッド AddHandler (add のサポート呼び出しに使用される既存のメソッドと同じもの) を使用して追加できます。ただし、一般に既存の WPFWPF ルーティング イベントは addremove のロジックのサポート実装を持つため、言語固有のイベント構文でルーティング イベントにハンドラーを追加できます。ヘルパー メソッドを使用するよりも、この構文の方がわかりやすく処理できます。Routed event handlers can always be added through a helper method AddHandler (which is the same method that the existing backing calls for add.) However, existing WPFWPF routed events generally have backing implementations of add and remove logic that allow the handlers for routed events to be added by a language-specific event syntax, which is more intuitive syntax than the helper method. ヘルパー メソッドの使用例を次に示します。The following is an example usage of the helper method:

void MakeButton()
     Button b2 = new Button();
     b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
 void Onb2Click(object sender, RoutedEventArgs e)
     //logic to handle the Click event
Private Sub MakeButton()
     Dim b2 As New Button()
     b2.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Onb2Click))
End Sub
 Private Sub Onb2Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
     'logic to handle the Click event     
 End Sub

C# の演算子構文を次の例に示します (Visual Basic の演算子構文は逆参照を処理するため少し異なります)。The next example shows the C# operator syntax (Visual Basic has slightly different operator syntax because of its handling of dereferencing):

void MakeButton2()
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
void Onb2Click2(object sender, RoutedEventArgs e)
  //logic to handle the Click event
Private Sub MakeButton2()
  Dim b2 As New Button()
  AddHandler b2.Click, AddressOf Onb2Click2
End Sub
Private Sub Onb2Click2(ByVal sender As Object, ByVal e As RoutedEventArgs)
  'logic to handle the Click event     
End Sub

コードでイベント ハンドラーを追加する方法の例については、「コードを使用してイベント ハンドラーを追加する」を参照してください。For an example of how to add an event handler in code, see Add an Event Handler Using Code.

Visual Basic を使用する場合、Handles キーワードを使用して、ハンドラー宣言の一部としてハンドラーを追加することもできます。If you are using Visual Basic, you can also use the Handles keyword to add handlers as part of the handler declarations. 詳細については、「Visual Basic と WPF のイベント処理」を参照してください。For more information, see Visual Basic and WPF Event Handling.

処理済みの概念The Concept of Handled

すべてのルーティング イベントは、共通のイベント データ基底クラス RoutedEventArgs を共有します。All routed events share a common event data base class, RoutedEventArgs. RoutedEventArgs を使用して、ブール値を受け取る Handled プロパティを定義します。RoutedEventArgs defines the Handled property, which takes a Boolean value. Handled プロパティの目的は、Handled の値を true に設定することにより、経路上の任意のイベント ハンドラーでルーティング イベントを "処理済み" としてマークできるようにすることです。The purpose of the Handled property is to enable any event handler along the route to mark the routed event as handled, by setting the value of Handled to true. 経路上の 1 つの要素のハンドラーで処理された後、共有イベント データが経路上の各リスナーに再び報告されます。After being processed by the handler at one element along the route, the shared event data is again reported to each listener along the route.

Handled の値は、ルーティング イベントが経路をたどる過程でどのように報告または処理されるかに影響します。The value of Handled affects how a routed event is reported or processed as it travels further along the route. ルーティング イベントのイベント データで Handledtrue の場合、その特定のイベント インスタンスでは以後、他の要素上でそのルーティング イベントをリッスンするハンドラーが呼び出されることは通常ありません。If Handled is true in the event data for a routed event, then handlers that listen for that routed event on other elements are generally no longer invoked for that particular event instance. これは、XAMLXAML でアタッチされたハンドラーの場合も、+=Handles など、言語固有のイベント ハンドラー アタッチ構文によって追加されたハンドラーの場合も同様です。This is true both for handlers attached in XAMLXAML and for handlers added by language-specific event handler attachment syntaxes such as += or Handles. 最も一般的なハンドラーのシナリオでは、Handledtrue に設定してイベントを処理済みとしてマークすると、トンネル ルーティングやバブル ルーティングだけでなく経路上でクラス ハンドラーによって処理されるすべてのイベントでもルーティングが "停止" されます。For most common handler scenarios, marking an event as handled by setting Handled to true will "stop" routing for either a tunneling route or a bubbling route, and also for any event that is handled at a point in the route by a class handler.

ただし、イベント データの Handledtrue であるルーティング イベントへの応答としてリスナーがハンドラーを実行できる "handledEventsToo" メカニズムも存在します。However, there is a "handledEventsToo" mechanism whereby listeners can still run handlers in response to routed events where Handled is true in the event data. つまり、イベント データを処理済みとしてマークしてもイベントのルーティングは完全には停止されません。In other words, the event route is not truly stopped by marking the event data as handled. handledEventsToo メカニズムは、コードまたは EventSetter でのみ使用できます。You can only use the handledEventsToo mechanism in code, or in an EventSetter:

ルーティング イベントの Handled 状態によって発生する動作に加え、Handled の概念は、アプリケーションの設計方法とイベント ハンドラー コードの記述方法に影響します。In addition to the behavior that Handled state produces in routed events, the concept of Handled has implications for how you should design your application and write the event handler code. Handled は、ルーティング イベントによって公開される簡単なプロトコルであると概念化することができます。You can conceptualize Handled as being a simple protocol that is exposed by routed events. このプロトコルの細かい使用方法は任意ですが、Handled の値の使用方法に関する概念設計は次のとおりです。Exactly how you use this protocol is up to you, but the conceptual design for how the value of Handled is intended to be used is as follows:

  • ルーティング イベントが処理済みとしてマークされている場合、このイベントを経路上の他の要素でもう一度処理する必要はありません。If a routed event is marked as handled, then it does not need to be handled again by other elements along that route.

  • ルーティング イベントが処理済みとしてマークされていない場合、経路上の前方に位置する他のリスナーがハンドラーを登録しないよう選択したか、登録済みハンドラーがイベント データを操作しないよう選択し、Handledtrue に設定したかのどちらかですIf a routed event is not marked as handled, then other listeners that were earlier along the route have chosen either not to register a handler, or the handlers that were registered chose not to manipulate the event data and set Handled to true. (または、現在のリスナーが経路上の出発点である可能性もあります)。現在のリスナーのハンドラーでは、次の 3 つのアクションを選択できます。(Or, it is of course possible that the current listener is the first point in the route.) Handlers on the current listener now have three possible courses of action:

    • アクションをまったく行いません。イベントは未処理のまま、次のリスナーにルーティングされます。Take no action at all; the event remains unhandled, and the event routes to the next listener.

    • イベントに応答してコードを実行しますが、実行したアクションはイベントを処理済みとしてマークするのに十分とは確定されません。Execute code in response to the event, but make the determination that the action taken was not substantial enough to warrant marking the event as handled. イベントは次のリスナーにルーティングされます。The event routes to the next listener.

    • イベントに応答してコードを実行します。Execute code in response to the event. 実行したアクションはイベントを処理済みとしてマークするのに十分と考えられるため、ハンドラーに渡されたイベント データでイベントを処理済みとしてマークします。Mark the event as handled in the event data passed to the handler, because the action taken was deemed substantial enough to warrant marking as handled. イベントは次のリスナーにルーティングされますが、イベント データに Handled=true があるため、他のハンドラーを呼び出す機会が与えられるのは handledEventsToo リスナーだけです。The event still routes to the next listener, but with Handled=true in its event data, so only handledEventsToo listeners have the opportunity to invoke further handlers.

この概念設計は、前に説明したルーティング動作により補強されます。そのため、経路上の前方のハンドラーによって Handledtrue に既に設定されている状況で、ルーティング イベントに対して呼び出されるルーティング イベントのハンドラーをアタッチすることは、さらに難しくなります (ただしコードやスタイルでは可能)。This conceptual design is reinforced by the routing behavior mentioned earlier: it is more difficult (although still possible in code or styles) to attach handlers for routed events that are invoked even if a previous handler along the route has already set Handled to true.

Handled、ルーティング イベントのクラス処理、およびルーティング イベントを Handled とマークすることが適切な場合の推奨事項の詳細については、「ルーティング イベントの処理済みとしてのマーキング、およびクラス処理」を参照してください。For more information about Handled, class handling of routed events, and recommendations about when it is appropriate to mark a routed event as Handled, see Marking Routed Events as Handled, and Class Handling.

アプリケーションでは、バブル ルーティング イベントを発生元のオブジェクトで処理するのが一般的であり、イベントのルーティング特性についてはまったく考慮されません。In applications, it is quite common to just handle a bubbling routed event on the object that raised it, and not be concerned with the event's routing characteristics at all. ただし、その場合でも、イベント データでルーティング イベントを処理済みとしてマークすることをお勧めします。そうすれば、要素ツリーの後方にある要素でそれと同じルーティング イベントにハンドラーがアタッチされている場合に起こりうる、予期しない副作用を回避できます。However, it is still a good practice to mark the routed event as handled in the event data, to prevent unanticipated side effects just in case an element that is further up the element tree also has a handler attached for that same routed event.

クラス ハンドラーClass Handlers

DependencyObject から派生したクラスを定義する場合、クラスに宣言または継承されたイベント メンバーであるルーティング イベントのクラス ハンドラーを定義およびアタッチすることもできます。If you are defining a class that derives in some way from DependencyObject, you can also define and attach a class handler for a routed event that is a declared or inherited event member of your class. ルーティング イベントが経路上の要素インスタンスに到達すると、クラスのインスタンスにアタッチされたどのインスタンス リスナー ハンドラーよりも先に、クラス ハンドラーが呼び出されます。Class handlers are invoked before any instance listener handlers that are attached to an instance of that class, whenever a routed event reaches an element instance in its route.

一部の WPFWPF コントロールには、特定のルーティング イベントに対する固有のクラス処理が存在します。Some WPFWPF controls have inherent class handling for certain routed events. このため、ルーティング イベントが発生していないように見えても、実際にはクラス処理されている場合があります。また、特定の手法を使用した場合、インスタンス ハンドラーでも処理される可能性があります。This might give the outward appearance that the routed event is not ever raised, but in reality it is being class handled, and the routed event can potentially still be handled by your instance handlers if you use certain techniques. また、多くの基底クラスとコントロールからは、クラス処理の動作をオーバーライドするために使用できる仮想メソッドが提供されています。Also, many base classes and controls expose virtual methods that can be used to override class handling behavior. 望ましくないクラス処理の回避方法とカスタム クラスを使った独自のクラス処理定義方法の詳細については、「ルーティング イベントの処理済みとしてのマーキング、およびクラス処理」を参照してください。For more information both on how to work around undesired class handling and on defining your own class handling in a custom class, see Marking Routed Events as Handled, and Class Handling.

WPF の添付イベントAttached Events in WPF

XAMLXAML 言語は、添付イベントと呼ばれる特殊な種類のイベントも定義します。The XAMLXAML language also defines a special type of event called an attached event. 添付イベントを使用して、任意の要素に特定のイベントのハンドラーを追加できます。An attached event enables you to add a handler for a particular event to an arbitrary element. イベントを処理する要素が添付イベントを定義または継承する必要はありません。また、イベントを発生させる可能性のあるオブジェクトでも、インスタンスを処理する添付先でも、そのイベントを定義したりクラス メンバーとして "所有" したりする必要はありません。The element handling the event need not define or inherit the attached event, and neither the object potentially raising the event nor the destination handling instance must define or otherwise "own" that event as a class member.

WPFWPF 入力システムでは、添付イベントを多用します。The WPFWPF input system uses attached events extensively. ただし、ほとんどすべての添付イベントは基本要素間で転送されます。However, nearly all of these attached events are forwarded through base elements. 入力イベントは、基本要素クラスのメンバーである、非添付のルーティング イベントに等しいものとして表されます。The input events then appear as equivalent non-attached routed events that are members of the base element class. たとえば、基になる添付イベント Mouse.MouseDown を特定の UIElement でより簡単に処理するには、添付イベント構文を XAMLXAML またはコードで使用するのではなく、その UIElementMouseDown を使用します。For instance, the underlying attached event Mouse.MouseDown can more easily be handled on any given UIElement by using MouseDown on that UIElement rather than dealing with attached event syntax either in XAMLXAML or code.

WPFWPF の添付イベントの詳細については、「添付イベントの概要」を参照してください。For more information about attached events in WPFWPF, see Attached Events Overview.

XAML の修飾イベント名Qualified Event Names in XAML

typename.eventname 添付イベント構文に似ているが厳密に言えば添付イベントの使用方法ではない、もう 1 つの構文使用方法では、子要素で発生したルーティング イベントのハンドラーをアタッチします。Another syntax usage that resembles typename.eventname attached event syntax but is not strictly speaking an attached event usage is when you attach handlers for routed events that are raised by child elements. 対応するルーティング イベントが共通の親要素にメンバーとして含まれない場合でも、共通の親要素にハンドラーをアタッチしてイベントのルーティングを利用します。You attach the handlers to a common parent, to take advantage of event routing, even though the common parent might not have the relevant routed event as a member. 次の例をもう一度検討します。Consider this example again:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>

ここでは、ハンドラーが追加された親要素のリスナーは StackPanel です。Here, the parent element listener where the handler is added is a StackPanel. ただし、追加しているのは、Button クラスで宣言され生成されるルーティング イベントのハンドラーです (実際には ButtonBase ですが、継承により Button に使用できます)。However, it is adding a handler for a routed event that was declared and will be raised by the Button class (ButtonBase actually, but available to Button through inheritance). Button がこのイベントを "所有" しますが、ルーティング イベント システムでは、任意のルーティング イベントのハンドラーを任意の UIElement インスタンス リスナー、またはそれ以外の場合であれば共通言語ランタイム (CLR) イベントのリスナーをアタッチできる ContentElement インスタンス リスナーにアタッチすることが許可されています。Button "owns" the event, but the routed event system permits handlers for any routed event to be attached to any UIElement or ContentElement instance listener that could otherwise attach listeners for a common language runtime (CLR) event. これらの修飾イベント属性名の既定の xmlns 名前空間は、通常、既定の WPFWPF xmlns 名前空間ですが、カスタム ルーティング イベント用のプレフィックスを持つ名前空間を指定することもできます。The default xmlns namespace for these qualified event attribute names is typically the default WPFWPF xmlns namespace, but you can also specify prefixed namespaces for custom routed events. xmlns の詳細については、「XAML 名前空間および WPF XAML の名前空間の割り当て」を参照してください。For more information about xmlns, see XAML Namespaces and Namespace Mapping for WPF XAML.

WPF の入力イベントWPF Input Events

WPFWPF プラットフォームにおけるルーティング イベントの主な使用例として、入力イベントがあります。One frequent application of routed events within the WPFWPF platform is for input events. WPFWPF では、トンネル ルーティング イベントの名前に "Preview" をプレフィックスとして付加するのが慣例です。In WPFWPF, tunneling routed events names are prefixed with the word "Preview" by convention. 入力イベントは、多くの場合、バブル イベントとトンネル イベントがペアになって構成されます。Input events often come in pairs, with one being the bubbling event and the other being the tunneling event. たとえば、KeyDown イベントと PreviewKeyDown イベントは同じシグネチャを持ち、前者がバブル入力イベント、後者がトンネル入力イベントです。For example, the KeyDown event and the PreviewKeyDown event have the same signature, with the former being the bubbling input event and the latter being the tunneling input event. 入力イベントによっては、バブル形式だけ、または直接ルーティング形式だけを持つ場合もあります。Occasionally, input events only have a bubbling version, or perhaps only a direct routed version. このドキュメント内のルーティング イベントに関するトピックでは、他のルーティング方法を使用する類似ルーティング イベントがある場合は相互参照を示します。またマネージド リファレンス ページでは、各ルーティング イベントのルーティング方法について説明します。In the documentation, routed event topics cross-reference similar routed events with alternative routing strategies if such routed events exist, and sections in the managed reference pages clarify the routing strategy of each routed event.

ペアになった WPFWPF 入力イベントを実装する場合、マウス ボタンを押すなどの入力による 1 つのユーザー操作で、ペアになった両方のルーティング イベントが順次発生するようにします。WPFWPF input events that come in pairs are implemented so that a single user action from input, such as a mouse button press, will raise both routed events of the pair in sequence. まず、トンネル イベントが発生し、その経路をたどります。First, the tunneling event is raised and travels its route. 次に、バブル イベントが発生し、その経路をたどります。Then the bubbling event is raised and travels its route. 2 つのイベントは、文字どおり同じイベント データ インスタンスを共有します。これは、バブル イベントを発生させた実装クラスの RaiseEvent メソッド呼び出しでは、トンネル イベントからのイベント データがリッスンされ、このデータが新たに発生するイベントで再利用されるためです。The two events literally share the same event data instance, because the RaiseEvent method call in the implementing class that raises the bubbling event listens for the event data from the tunneling event and reuses it in the new raised event. トンネル イベントのハンドラーを持つリスナーは、ルーティング イベントを処理済みとしてマークする機会を最初に与えられます (最初がクラス ハンドラーで、その次がインスタンス ハンドラーです)。Listeners with handlers for the tunneling event have the first opportunity to mark the routed event handled (class handlers first, then instance handlers). トンネル経路上の要素がルーティング イベントを処理済みとしてマークした場合、処理済みイベントのデータがバブル イベントに送信され、同等のバブル入力イベントにアタッチされた標準のハンドラーは呼び出されません。If an element along the tunneling route marked the routed event as handled, the already-handled event data is sent on for the bubbling event, and typical handlers attached for the equivalent bubbling input events will not be invoked. 外部からは、処理済みのバブル イベントが発生しなかったように見えます。To outward appearances it will be as if the handled bubbling event has not even been raised. この処理動作が便利なのは、コントロールの複合化の場合です。この場合、すべてのヒット テスト ベースの入力イベントまたはフォーカス ベースの入力イベントが、コントロールの複合部分ではなく最終的なコントロールから報告される必要があるためです。This handling behavior is useful for control compositing, where you might want all hit-test based input events or focus-based input events to be reported by your final control, rather than its composite parts. 最終的なコントロール要素は、複合のルート近くにあるため、トンネル イベントを最初にクラス処理し、コントロール クラスをサポートするコードの一部としてそのルーティング イベントをコントロール固有のイベントで "置き換える" 機会があります。The final control element is closer to the root in the compositing, and therefore has the opportunity to class handle the tunneling event first and perhaps to "replace" that routed event with a more control-specific event, as part of the code that backs the control class.

入力イベント処理のしくみを説明するため、次の入力イベント例について考えます。As an illustration of how input event processing works, consider the following input event example. 次のツリー図の leaf element #2 は、PreviewMouseDownMouseDown の両方のイベントの発生元です。In the following tree illustration, leaf element #2 is the source of both a PreviewMouseDown and then a MouseDown event:

イベント ルーティング ダイアグラム

次の順序でイベントが処理されます。The order of event processing is as follows:

  1. ルート要素の PreviewMouseDown (トンネル)。PreviewMouseDown (tunnel) on root element.

  2. 中間要素 #1 の PreviewMouseDown (トンネル)。PreviewMouseDown (tunnel) on intermediate element #1.

  3. ソース要素 #2 の PreviewMouseDown (トンネル)。PreviewMouseDown (tunnel) on source element #2.

  4. ソース要素 #2 の MouseDown (バブル)。MouseDown (bubble) on source element #2.

  5. 中間要素 #1 の MouseDown (バブル)。MouseDown (bubble) on intermediate element #1.

  6. ルート要素の MouseDown (バブル)。MouseDown (bubble) on root element.

ルーティング イベント ハンドラー デリゲートは、2 つのオブジェクトへの参照を提供します。その 2 つは、イベントを発生したオブジェクトと、ハンドラーが呼び出されたオブジェクトです。A routed event handler delegate provides references to two objects: the object that raised the event and the object where the handler was invoked. ハンドラーが呼び出されたオブジェクトは、sender パラメーターによって報告されるオブジェクトです。The object where the handler was invoked is the object reported by the sender parameter. イベントが最初に発生したオブジェクトは、イベント データの Source プロパティによって報告されます。The object where the event was first raised is reported by the Source property in the event data. ルーティング イベントを発生させるオブジェクトと処理するオブジェクトが同じ場合もあります。その場合、senderSource は同じです (前のイベント処理例の手順 3 と手順 4 がこれに該当します)。A routed event can still be raised and handled by the same object, in which case sender and Source are identical (this is the case with Steps 3 and 4 in the event processing example list).

トンネルとバブルにより、親要素は、Source がその子要素のいずれかである入力イベントを受け取ります。Because of tunneling and bubbling, parent elements receive input events where the Source is one of their child elements. どれがソース要素であるかを知る必要がある場合、Source プロパティにアクセスするとソース要素を特定できます。When it is important to know what the source element is, you can identify the source element by accessing the Source property.

通常、いったん入力イベントが Handled としてマークされると、ハンドラーはそれ以上呼び出されません。Usually, once the input event is marked Handled, further handlers are not invoked. 一般に、入力イベントが意味することをアプリケーション固有の方法で論理処理するハンドラーが呼び出されたら、直ちに入力イベントを処理済みとしてマークする必要があります。Typically, you should mark input events as handled as soon as a handler is invoked that addresses your application-specific logical handling of the meaning of the input event.

Handled 状態に関するこの一般論に対する例外として、イベント データの Handled 状態を意図的に無視するよう登録された入力イベント ハンドラーは、どちらの経路上でも呼び出されます。The exception to this general statement about Handled state is that input event handlers that are registered to deliberately ignore Handled state of the event data would still be invoked along either route. 詳細については、「プレビュー イベント」または「ルーティング イベントの処理済みとしてのマーキング、およびクラス処理」を参照してください。For more information, see Preview Events or Marking Routed Events as Handled, and Class Handling.

トンネル イベントとバブル イベント間の共有イベント データ モデルの概念も、トンネル イベントとバブル イベントの順次発生の概念も、すべてのルーティング イベントに当てはまるとは限りません。The shared event data model between tunneling and bubbling events, and the sequential raising of first tunneling then bubbling events, is not a concept that is generally true for all routed events. そうした動作は、ペアになった入力イベントを WPFWPF 入力デバイスがどのように発生させ接続するよう選択するかによって、個別に実装されます。That behavior is specifically implemented by how WPFWPF input devices choose to raise and connect the input event pairs. 独自の入力イベントの実装は高度なシナリオですが、独自の入力イベントでそうしたモデルを採用することもできます。Implementing your own input events is an advanced scenario, but you might choose to follow that model for your own input events also.

一部のクラスでは、特定の入力イベントをクラス処理します。通常、その目的は、ユーザーによる特定の入力イベントの意味をそのコントロール内で再定義して、新しいイベントを発生させることです。Certain classes choose to class-handle certain input events, usually with the intent of redefining what a particular user-driven input event means within that control and raising a new event. 詳細については、「ルーティング イベントの処理済みとしてのマーキング、およびクラス処理」を参照してください。For more information, see Marking Routed Events as Handled, and Class Handling.

入力と一般的なアプリケーション シナリオにおける入力とイベントの対話方法の詳細については、「入力の概要」を参照してください。For more information on input and how input and events interact in typical application scenarios, see Input Overview.

EventSetter と EventTriggerEventSetters and EventTriggers

EventSetter を使用すると、スタイルのマークアップ内に、あらかじめ宣言された XAMLXAML イベント処理構文を含めることができます。In styles, you can include some pre-declared XAMLXAML event handling syntax in the markup by using an EventSetter. そのスタイルを適用すると、参照されているハンドラーが、スタイルが適用されるインスタンスに追加されます。When the style is applied, the referenced handler is added to the styled instance. EventSetter は、ルーティング イベントに対してのみ宣言できます。You can declare an EventSetter only for a routed event. 次に例を示します。The following is an example. ここで参照されている b1SetColor メソッドは、分離コード ファイル内にあります。Note that the b1SetColor method referenced here is in a code-behind file.

    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">
    Raise event, handle it, use handled=true handler to get it anyway.

ここでの利点は、スタイルに、アプリケーションの任意のボタンに適用可能なその他の情報が大量に含まれている可能性があることです。そのスタイルに EventSetter を含めることによって、マークアップ レベルでもコードの再利用が促進されます。The advantage gained here is that the style is likely to contain a great deal of other information that could apply to any button in your application, and having the EventSetter be part of that style promotes code reuse even at the markup level. また、EventSetter によって、一般的なアプリケーションやページのマークアップよりも一歩進んだハンドラーのメソッド名の抽象化が実現されます。Also, an EventSetter abstracts method names for handlers one step further away from the general application and page markup.

ルーティング イベントと WPFWPF アニメーション機能を結合したもう 1 つの特別な構文が、EventTrigger です。Another specialized syntax that combines the routed event and animation features of WPFWPF is an EventTrigger. EventSetter と同様、EventTrigger にもルーティング イベントのみを使用できます。As with EventSetter, only routed events may be used for an EventTrigger. 通常、EventTrigger はスタイルの一部として宣言しますが、EventTrigger をページ レベルの要素で Triggers コレクションの一部として (つまり ControlTemplate 内で) 宣言することもできます。Typically, an EventTrigger is declared as part of a style, but an EventTrigger can also be declared on page-level elements as part of the Triggers collection, or in a ControlTemplate. EventTrigger を使用すると、ルーティング イベントに対する EventTrigger を宣言した要素にそのイベントが経路で到達するたびに Storyboard が実行されるよう指定できます。An EventTrigger enables you to specify a Storyboard that runs whenever a routed event reaches an element in its route that declares an EventTrigger for that event. 単にイベントを処理して既存のストーリーボードが開始されるようにする方法と比べて EventTrigger が優れている点は、EventTrigger の方がストーリーボードとそのランタイム動作をよりよく制御できることです。The advantage of an EventTrigger over just handling the event and causing it to start an existing storyboard is that an EventTrigger provides better control over the storyboard and its run-time behavior. 詳細については、「開始後のストーリーボードをイベント トリガーを使用して制御する」を参照してください。For more information, see Use Event Triggers to Control a Storyboard After It Starts.

ルーティング イベントの詳細More About Routed Events

このトピックの主な目的は、ルーティング イベントの基本概念を説明し、さまざまな基本要素や基本コントロール内の既存のルーティング イベントに応答する方法とタイミングについて解説することです。This topic mainly discusses routed events from the perspective of describing the basic concepts and offering guidance on how and when to respond to the routed events that are already present in the various base elements and controls. しかし、独自のルーティング イベントを、特殊なイベント データ クラスやデリゲートなど、必要な支援機能すべてと共に、カスタム クラスに作成することもできます。However, you can create your own routed event on your custom class along with all the necessary support, such as specialized event data classes and delegates. ルーティング イベントの所有者はどのクラスでもかまいませんが、利便性のため、ルーティング イベントの発生と処理は UIElement または ContentElement の派生クラスで行われる必要があります。The routed event owner can be any class, but routed events must be raised by and handled by UIElement or ContentElement derived classes in order to be useful. カスタム イベントの詳細については、「カスタム ルーティング イベントを作成する」を参照してください。For more information about custom events, see Create a Custom Routed Event.

関連項目See also