次の方法で共有


カスタム ルーティング イベントを作成する方法

Windows Presentation Foundation (WPF) アプリケーション開発者とコンポーネント作成者は、カスタム ルーティング イベントを作成して、共通言語ランタイム (CLR) イベントの機能を拡張できます。 ルーティング イベント機能の詳細については、「ルーティング イベントを 使用する理由」を参照してください。 この記事では、カスタム ルーティング イベントの作成の基本について説明します。

[前提条件]

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

ルーティングされたイベントの手順

ルーティング イベントを作成する基本的な手順は次のとおりです。

  1. RoutedEvent メソッドを使用してRegisterRoutedEventを登録します。

  2. 登録呼び出しは、登録されたイベント名、RoutedEvent、およびその他のイベントの詳細を保持するルーティング イベント識別子と呼ばれる インスタンスを返します。 静的な読み取り専用フィールドに識別子を割り当てます。 慣例:

    • バブル戦略を持つルーティング イベントの識別子には、<event name>Eventという名前が付けられます。 たとえば、イベント名が Tap 場合、識別子には TapEvent という名前を付ける必要があります。
    • トンネリング戦略を使用したルーティング イベントの識別子には、Preview<event name>Eventという名前が付けられます。 たとえば、イベント名が Tap 場合、識別子には PreviewTapEvent という名前を付ける必要があります。
  3. CLRイベントのaddおよびremoveアクセサーを定義します。 CLR イベント アクセサーがない場合は、 UIElement.AddHandler メソッドと UIElement.RemoveHandler メソッドへの直接呼び出しによってのみ、イベント ハンドラーを追加または削除できます。 CLR イベント アクセサーを使用すると、次のイベント ハンドラーの割り当てメカニズムを取得できます。

    • 拡張アプリケーション マークアップ言語 (XAML) では、属性構文を使用してイベント ハンドラーを追加できます。
    • C# の場合、 += 演算子と -= 演算子を使用して、イベント ハンドラーを追加または削除できます。
    • VB の場合、 AddHandler ステートメントと RemoveHandler ステートメントを使用して、イベント ハンドラーを追加または削除できます。
  4. ルーティング イベントをトリガーするためのカスタム ロジックを追加します。 たとえば、ロジックによって、ユーザー入力とアプリケーションの状態に基づいてイベントがトリガーされる場合があります。

次の例では、カスタム コントロール ライブラリに CustomButton クラスを実装します。 CustomButtonから派生する Button クラス:

  1. RoutedEvent メソッドを使用して ConditionalClick という名前のRegisterRoutedEventを登録し、登録時のバブル戦略を指定します。
  2. 登録呼び出しから返された RoutedEvent インスタンスを、 ConditionalClickEventという名前の静的な読み取り専用フィールドに割り当てます。
  3. CLR のイベント アクセサーの 追加削除 を定義します。
  4. CustomButtonがクリックされ、外部条件が適用されたときにカスタム ルーティング イベントを発生させるカスタム ロジックを追加します。 このコード例では、オーバーライドされたConditionalClick仮想メソッド内からOnClickルーティング イベントを発生させますが、任意の方法でイベントを発生させることができます。
public class CustomButton : Button
{
    // Register a custom routed event using the Bubble routing strategy.
    public static readonly RoutedEvent ConditionalClickEvent = EventManager.RegisterRoutedEvent(
        name: "ConditionalClick",
        routingStrategy: RoutingStrategy.Bubble,
        handlerType: typeof(RoutedEventHandler),
        ownerType: typeof(CustomButton));

    // Provide CLR accessors for assigning an event handler.
    public event RoutedEventHandler ConditionalClick
    {
        add { AddHandler(ConditionalClickEvent, value); }
        remove { RemoveHandler(ConditionalClickEvent, value); }
    }

    void RaiseCustomRoutedEvent()
    {
        // Create a RoutedEventArgs instance.
        RoutedEventArgs routedEventArgs = new(routedEvent: ConditionalClickEvent);

        // Raise the event, which will bubble up through the element tree.
        RaiseEvent(routedEventArgs);
    }

    // For demo purposes, we use the Click event as a trigger.
    protected override void OnClick()
    {
        // Some condition combined with the Click event will trigger the ConditionalClick event.
        if (DateTime.Now > new DateTime())
            RaiseCustomRoutedEvent();

        // Call the base class OnClick() method so Click event subscribers are notified.
        base.OnClick();
    }
}
Public Class CustomButton
    Inherits Button

    ' Register a custom routed event with the Bubble routing strategy.
    Public Shared ReadOnly ConditionalClickEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
        name:="ConditionalClick",
        routingStrategy:=RoutingStrategy.Bubble,
        handlerType:=GetType(RoutedEventHandler),
        ownerType:=GetType(CustomButton))

    ' Provide CLR accessors to support event handler assignment.
    Public Custom Event ConditionalClick As RoutedEventHandler

        AddHandler(value As RoutedEventHandler)
            [AddHandler](ConditionalClickEvent, value)
        End AddHandler

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

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

    End Event

    Private Sub RaiseCustomRoutedEvent()

        ' Create a RoutedEventArgs instance.
        Dim routedEventArgs As New RoutedEventArgs(routedEvent:=ConditionalClickEvent)

        ' Raise the event, which will bubble up through the element tree.
        [RaiseEvent](routedEventArgs)

    End Sub

    ' For demo purposes, we use the Click event as a trigger.
    Protected Overrides Sub OnClick()

        ' Some condition combined with the Click event will trigger the ConditionalClick event.
        If Date.Now > New DateTime() Then RaiseCustomRoutedEvent()

        ' Call the base class OnClick() method so Click event subscribers are notified.
        MyBase.OnClick()

    End Sub
End Class

この例には、XAML マークアップを使用してCustomButtonのインスタンスをStackPanelに追加し、Handler_ConditionalClick要素とConditionalClick要素のCustomButton イベント ハンドラーとしてStackPanel1 メソッドを割り当てる別の WPF アプリケーションが含まれています。

<Window x:Class="CodeSample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:WpfControl;assembly=WpfControlLibrary"
        Title="How to create a custom routed event" Height="100" Width="300">

    <StackPanel Name="StackPanel1" custom:CustomButton.ConditionalClick="Handler_ConditionalClick">
        <custom:CustomButton
            Name="customButton"
            ConditionalClick="Handler_ConditionalClick"
            Content="Click to trigger a custom routed event"
            Background="LightGray">
        </custom:CustomButton>
    </StackPanel>
</Window>

分離コードでは、WPF アプリケーションによって Handler_ConditionalClick イベント ハンドラー メソッドが定義されます。 イベント ハンドラー メソッドは、分離コードでのみ実装できます。

// The ConditionalClick event handler.
private void Handler_ConditionalClick(object sender, RoutedEventArgs e)
{
    string senderName = ((FrameworkElement)sender).Name;
    string sourceName = ((FrameworkElement)e.Source).Name;

    Debug.WriteLine($"Routed event handler attached to {senderName}, " +
        $"triggered by the ConditionalClick routed event raised on {sourceName}.");
}

// Debug output when CustomButton is clicked:
// Routed event handler attached to CustomButton,
//     triggered by the ConditionalClick routed event raised on CustomButton.
// Routed event handler attached to StackPanel1,
//     triggered by the ConditionalClick routed event raised on CustomButton.
' The ConditionalClick event handler.
Private Sub Handler_ConditionalClick(sender As Object, e As RoutedEventArgs)

    Dim sourceName As String = CType(e.Source, FrameworkElement).Name
    Dim senderName As String = CType(sender, FrameworkElement).Name

    Debug.WriteLine($"Routed event handler attached to {senderName}, " +
        $"triggered by the ConditionalClick routed event raised on {sourceName}.")

End Sub

' Debug output when CustomButton is clicked:
' Routed event handler attached to CustomButton,
'     triggered by the ConditionalClick routed event raised on CustomButton.
' Routed event handler attached to StackPanel1,
'     triggered by the ConditionalClick routed event raised on CustomButton.

CustomButtonがクリックされたとき:

  1. ConditionalClickルーティング イベントは、CustomButtonで発生します。
  2. Handler_ConditionalClickにアタッチされたCustomButton イベント ハンドラーがトリガーされます。
  3. ConditionalClickルーティング イベントは、要素ツリーを上にたどってStackPanel1します。
  4. Handler_ConditionalClickにアタッチされたStackPanel1 イベント ハンドラーがトリガーされます。
  5. ConditionalClickルーティング イベントは、他の走査された要素にアタッチされている他のConditionalClick イベント ハンドラーをトリガーする可能性がある要素ツリーを上に続けます。

Handler_ConditionalClick イベント ハンドラーは、イベントをトリガーしたイベントに関する次の情報を取得します。

  • sender オブジェクト。イベント ハンドラーがアタッチされている要素です。 senderは、ハンドラーの初回実行時にCustomButtonされ、2 回目の実行時にStackPanel1されます。
  • RoutedEventArgs.Source オブジェクト。これは、最初にイベントを発生させた要素です。 この例では、 Source は常に CustomButton

ルーティング イベントと CLR イベントの主な違いは、ルーティング イベントが要素ツリーを走査し、ハンドラーを探すのに対し、CLR イベントは要素ツリーを走査せず、ハンドラーはイベントを発生させたソース オブジェクトにのみアタッチできることです。 その結果、ルーティング イベント sender は、要素ツリー内の任意の走査された要素にすることができます。

イベント登録呼び出しでルーティング戦略を Tunnel に設定する以外は、バブル イベントと同じ方法でトンネリング イベントを作成できます。 トンネリング イベントの詳細については、「 WPF 入力イベント」を参照してください。

こちらも参照ください