Version: 2019.1
イベントのディスパッチ
イベントの統合

イベントへの応答

受信するイベントに対して、ビジュアル要素を 2 通りの方法で反応させることができます。

  • イベントコールバックを登録する方法
  • デフォルトのアクションを実装する方法

イベントコールバックを登録するかデフォルトのアクションを実装するかは、多くの要素によって異なります。

例えば、既存のクラスからオブジェクトをインスタンス化し、イベントを受け取ったときにそのインスタンスが特定の方法で反応するようにしたい場合、唯一のオプションはこのインスタンスにコールバックを登録することです。

ただし、VisualElement から新しいクラスを派生させ、このクラスのすべてのインスタンスが同じようにイベントに反応するようにするには、コンストラクターでこのクラスのすべてのインスタンスにコールバックを登録するか、そのクラスのデフォルトのアクションを実装します。

コールバックとデフォルトアクションの基本的な違いは以下のとおりです。

  • コールバックはクラスのインスタンスに登録する必要があります。デフォルトアクションは、クラスの仮想関数として実装されます。
  • 伝播経路のすべての要素に対してコールバックが実行されます。デフォルトのアクションは、イベントターゲットに対してのみ実行されます。

event.PreventDefault() を呼び出して、デフォルトアクションの実行を避けることができます。ただし、一部のイベントタイプはキャンセルできません。そのため、デフォルトのアクションはキャンセルできません。コールバックが実行されるのを防ぐには、event.StopPropagation() または event.StopImmediatePropagation() を呼び出します。キャンセル不可のイベントの場合も同様です。

デフォルトアクションは、特定タイプの要素がイベントを受け取ったときに行う動作と考える必要があります。

例えば、チェックボックスは、その状態が切り替わることによってクリックイベントに応答する必要があります。この動作は、すべてのチェックボックスにコールバックを登録するのではなく、デフォルトアクションの仮想関数をオーバーライドすることによって実装する必要があります。

一般的に、デフォルトのアクションでは、要素に通常期待される動作の実装を行うことを推奨します。このようにすると、インスタンスにアタッチされたコールバックの PreventDefault() を呼び出すことで、インスタンスごとに要素のデフォルトの動作を確実にキャンセルすることができます。

デフォルトのアクションとして動作を実装するその他の利点は、実行するためにコールバックのレジストリで検索を必要とせず、コールバックのないインスタンスを降下/上昇の伝搬プロセスから効率的に利用できるということです。

柔軟性を高めるために、イベントターゲットのデフォルトのアクションは、イベントのディスパッチ処理中に 2 か所で実行されます。

  • 下降と上昇の伝播段階の間で、ターゲットのコールバックが実行された直後に、ExecuteDefaultActionsAtTarget() をオーバーライドします。
  • イベントのディスパッチ処理の最後に、ExecuteDefaultActions() をオーバーライドします。

可能な場合は常に、クラスのデフォルトアクションを ExecuteDefaultActions() に実装します。このようにすると、それらをオーバーライドするより多くのオプションを作ることができます。PreventDefault() は、イベント伝播プロセスの下降段階または上昇段階のいずれかに呼び出すことができます。

ただし、クラスのデフォルトアクションを実行するイベントが、要素の親に伝播しないように強制しなければならない場合もあります。例えば、テキストフィールドは値を変更する Key Down イベントを受け取ります。これらの Key Down イベントは、例えば、Delete キーを使用してコンテンツを削除する可能性がある親には伝播されません。この場合、 ExecuteDefaultActionsAtTarget() を使ってデフォルトのアクションを実装し、StopPropagation() を呼び出します。このようにして、イベントが上昇段階でコールバックによって処理されないようにします。

デフォルトのアクションは、イベントのターゲットに対してのみ実行されます。デフォルトのアクションは、イベント伝搬の結果として、要素がイベントを受け取ったときには実行されません。クラスの子や親をターゲットとするイベントにクラスを応答させたい場合は、クラスの各インスタンスにコールバックを登録する必要があります。これは必要な場合もありますが、拡張性とパフォーマンスをより効率的にするには、クラスのすべてのインスタンスにコールバックを登録することは避けてください。

イベントコールバックの登録

カスタム動作をビジュアル要素の特定のインスタンスに追加するには、カスタム動作を引き起こすイベントのコールバックを登録する必要があります。

イベントのコールバックを登録する利点は、既存のクラスの個々のインスタンスの動作をカスタマイズできることです。イベントコールバックを登録すると、実行に時間がかかるためパフォーマンスが低下するという欠点があります。イベントが受信されるたびに登録されたイベントが確認され、実行するコールバックを決定するため、実行に時間がかかります。

例えば、以下のコードは MouseDownEvent の 2 つのコールバックを登録します。

    // mouse down イベントのコールバックを登録
        myElement.RegisterCallback<MouseDownEvent>(MyCallback);
        // 上と同様。ただし、コールバックにユーザーデータの送信も行います
        myElement.RegisterCallback<MouseDownEvent, MyType>(MyCallbackWithData, myData);

コールバックのシグネチャは以下のようになります。

void MyCallback(MouseDownEvent evt) { /* ... */ }
void MyCallbackWithData(MouseDownEvent evt, MyType data) { /* ... */ }

イベントに必要な数のコールバックを無限に登録することができます。ただし、コールバック登録システムでは、指定したイベントと伝播段階で同じコールバック関数を複数回登録することはできません。myElement.UnregisterCallback() メソッドを呼び出すことによって、VisualElement からコールバックを削除することができます。

伝播経路でターゲットに到達するまでの各要素 (ターゲットの親要素) は、イベントを 2 回受け取ります。ターゲットまでの下降段階中に 1 回、上昇段階中に 1 回です。登録されたコールバックを 2 回実行しないようにするには、オプションの RegisterCallback を使用して、どの段階でコールバックを実行するかを指定します。

デフォルトでは、登録されたコールバックはターゲットの段階と上昇段階で実行されます。このデフォルトの動作では、親要素がその子の後に反応します。例えば、最初に親を反応させたい場合は、その子の動作をオーバーライドするために、コールバックを TrickleDown.TrickleDown オプションで登録します。

    // 下降段階のコールバックを登録
        myElement.RegisterCallback<MouseDownEvent>(MyCallback, TrickleDown.TrickleDown);

これは、ターゲット段階と下降段階でコールバックを実行するようにディスパッチャーに指示します。

デフォルトアクションの実装

デフォルトのアクションはクラスのすべてのインスタンスに適用されます。つまり、要素が受け取るイベントごとに 1 つまたは両方のメソッドが呼び出されます。

デフォルトのアクションを実装するクラスは、そのインスタンスにコールバックを登録することもできます。

イベントクラスは、イベント処理の前後いずれかに実行される動作を実装します。これを行うために、イベントクラスは、EventBase クラスの PostDispatch()PreDispatch()メソッドをオーバーライドします。イベントはキューに置かれているので、これらのメソッドは、イベントが発生したときではなく、イベントが処理されるときに状態を更新したり、タスクを実行します。例えば、BlurEvent は要素を処理する (アクションを実行する) 前に現在フォーカスがある要素を変更します。

デフォルトのアクションを実装するには、VisualElement の新しいサブクラスを派生させ、ExecuteDefaultActionAtTarget() メソッド、ExecuteDefaultAction()メソッドのいずれか、または両方を実装する必要があります。

デフォルトのアクションは、イベントを受け取ったときに、ビジュアル要素のサブクラスの各インスタンスによって実行されるアクションです。ExecuteDefaultActionAtTarget()ExecuteDefaultAction() をオーバーライドすることで、デフォルトのアクションをカスタマイズすることができます。

override void ExecuteDefaultActionAtTarget(EventBase evt)
{
    // 基本関数の呼び出しを忘れないように
    base.ExecuteDefaultActionAtTarget(evt);

    if (evt.GetEventTypeId() == MouseDownEvent.TypeId())
    {
        // ...
    }
    else if (evt.GetEventTypeId() == MouseUpEvent.TypeId())
    {
        // ...
    }
    // More event types
}

柔軟性をより高めるために、デフォルトアクションを ExecuteDefaultAction() に実装する必要があります。これにより、ユーザーは必要に応じて、デフォルトのアクションの実行を停止または防止できます。

ターゲットのデフォルトアクションを親のコールバックの前に実行したい場合は、デフォルトアクションを ExecuteDefaultActionAtTarget() に実装します。

イベント処理順序

イベントが発生すると、ビジュアル要素ツリーの伝播経路に沿って送信されます。イベントタイプがイベント伝播のすべての段階に従うことが必須と仮定すると、イベントは各ビジュアル要素に送信されます。イベントの処理順序は次のとおりです。

  1. ルート要素からイベントターゲットの親まで、要素のイベントコールバックを実行します。これはディスパッチ処理の 下降段階 です。
  2. イベントターゲットでイベントコールバックを実行します。これはディスパッチ処理の ターゲット段階 です。
  3. ターゲットの ExecuteDefaultActionAtTarget() を呼び出します。
  4. イベントターゲットの親からルートまでの要素のイベントコールバックを実行します。これはディスパッチ処理の 上昇段階 です。
  5. ターゲットの ExecuteDefaultAction() を呼び出します。

イベントが伝播経路に沿って送信されるとき、Event.currentTarget は更新され、イベントを現在処理している要素になります。これは、イベントコールバック関数内で、Event.currentTarget はコールバックが登録される要素であり、Event.target はイベントが発生する要素であることを意味します。

イベント伝播の停止とデフォルトのアクションのキャンセル

コールバックを使用して、アクションの実行を停止、防止、キャンセルすることができます。ただし、EventBase.PreDispatch()EventBase.PostDispatch() メソッドの実行を防ぐことはできません。

以下のメソッドは、イベントの伝播とデフォルトのアクションに影響します。

  • StopImmediatePropagation() はすぐにイベントの伝播処理を停止します。このイベントに対してこれ以降のコールバックはすべて実行されません。ただし、 ExecuteDefaultActionAtTarget()ExecuteDefaultAction() のデフォルトアクションだけは引き続き実行されます。
  • StopPropagation() は、現在の要素の最後のコールバックの後のイベント伝播処理を停止します。これにより、現在の要素のすべてのコールバックが確実に実行されますが、それ以降の要素はイベントに反応しません。ExecuteDefaultActionAtTarget()ExecuteDefaultAction() のデフォルトアクションだけは引き続き実行されます。
  • PreventDefaultAction() は、ExecuteDefaultActionAtTarget()ExecuteDefaultAction() デフォルトアクションが呼び出されることを防ぎます。PreventDefaultAction() は他のコールバックの実行は防ぎません。さらに、上昇段階で PreventDefaultAction() を呼び出しても、ExecuteDefaultActionAtTarget() のデフォルトアクションを防げません。なぜなら、すでに実行済みだからです。ExecuteDefaultActionAtTarget() のデフォルトアクションは、下降段階の間に実行されます。

イベントのディスパッチ
イベントの統合