ビジュアル層でのヒット テスト

ここでは、ビジュアル層で提供されるヒット テスト機能の概要について説明します。 ヒット テストのサポートにより、ジオメトリまたはポイント値が Visual のレンダリングされるコンテンツ内にあるかどうかを判断でき、複数のオブジェクトを選択するための選択四角形などのユーザー インターフェイスの動作を実装できます。

このトピックは、次のセクションで構成されています。

  • ヒット テストのシナリオ
  • ヒット テストのサポート
  • ヒット テストと Z オーダー
  • 既定のヒット テストの使用
  • ヒット テスト結果のコールバックの使用
  • ヒット テスト フィルターのコールバックの使用
  • 既定のヒット テストのオーバーライド
  • 関連トピック

ヒット テストのシナリオ

UIElement クラスには InputHitTest メソッドが用意されています。このメソッドを使用すると、指定した座標値を使用して要素に対してヒット テストを実行できます。 多くの場合、InputHitTest メソッドは、要素のヒット テストを実装するために必要な機能を提供します。 ただし、ビジュアル層でのヒット テストを実装する必要があるシナリオもいくつか存在します。

  • UIElement 以外のオブジェクトに対するヒット テスト : これは、DrawingVisual やグラフィックス オブジェクトなどの UIElement 以外のオブジェクトのヒット テストを行う場合に適用されます。

  • ジオメトリを使用したヒット テスト : これは、ポイントの座標値ではなくジオメトリ オブジェクトを使用してヒット テストを行う必要がある場合に適用されます。

  • 複数のオブジェクトに対するヒット テスト : これは、重なっているオブジェクトなどの複数のオブジェクトに対してヒット テストを行う必要がある場合に適用されます。 最初のビジュアルだけでなく、ジオメトリまたはポイントと交差するすべてのビジュアルの結果を取得できます。

  • UIElement ヒット テスト ポリシーの無視 : これは、要素が無効または非表示かなどについて考慮する UIElement ヒット テスト ポリシーを無視する必要がある場合に適用されます。

メモメモ

ビジュアル層でのヒット テストを示すコード サンプル全体については、DrawingVisuals を使用したヒット テストのサンプルおよび Win32 相互運用によるヒット テストのサンプルを参照してください。

ヒット テストのサポート

VisualTreeHelper クラスの HitTest メソッドの目的は、ジオメトリまたはポイントの座標値が特定のオブジェクト (コントロールやグラフィック要素など) のレンダリングされるコンテンツ内にあるかどうかを確認することです。 たとえば、ヒット テストを使用することで、オブジェクトの外接する四角形内でのマウス クリックが円のジオメトリ内にあるかどうかを確認できます。 また、ヒット テストの既定の実装をオーバーライドして、独自のカスタム ヒット テスト計算を実行することもできます。

四角形以外のオブジェクトの領域と外接する四角形との関係を次の図に示します。

有効なヒット テスト領域の図

有効なヒット テスト領域のダイアグラム

ヒット テストと Z オーダー

Windows Presentation Foundation (WPF) のビジュアル層は、最上位のオブジェクトだけでなく、ポイントまたはジオメトリの下にあるすべてのオブジェクトに対するヒット テストをサポートします。 結果は z オーダーで返されます。 ただし、HitTest メソッドにパラメーターとして渡すビジュアル オブジェクトによって、ビジュアル ツリーのどの部分がヒット テストの対象となるかが決定されます。 ヒット テストは、ビジュアル ツリー全体またはその一部に対して実行できます。

次の図では、四角形と三角形の両方のオブジェクト上に円オブジェクトがあります。 z オーダーの値が最上位のビジュアル オブジェクトのヒット テストのみを行う場合、HitTestResultCallback から Stop を返すようにビジュアル ヒット テストの列挙体を設定して、最初の項目の終了後にヒット テストの移動を停止することができます。

ビジュアル ツリーの z オーダーの図

ビジュアル ツリーの z オーダーのダイアグラム

特定のポイントまたはジオメトリの下にあるすべてのビジュアル オブジェクトを列挙する場合は、HitTestResultCallback から Continue を返します。 つまり、完全に隠されているものも含めて、他のオブジェクトの下にあるすべてのビジュアル オブジェクトに対してヒット テストを行うことができます。 詳細については、「ヒット テスト結果のコールバックの使用」のサンプル コードを参照してください。

メモメモ

透明なビジュアル オブジェクトのヒット テストも実行できます。

既定のヒット テストの使用

HitTest メソッドを使用して、テストを実行するビジュアル オブジェクトとポイントの座標値を指定することにより、ビジュアル オブジェクトのジオメトリ内にポイントがあるかどうかを識別できます。 ビジュアル オブジェクトのパラメーターは、ヒット テストの検索のためのビジュアル ツリー内の開始点を識別します。 ジオメトリに座標が含まれるビジュアル ツリーにビジュアル オブジェクトが存在する場合は、HitTestResult オブジェクトの VisualHit プロパティに設定されます。 次に、HitTestResultHitTest メソッドから返されます。 ヒット テストの対象のビジュアル サブツリーにポイントが含まれない場合、HitTest は null を返します。

メモメモ

既定のヒット テストでは、常に z オーダーで最上位のオブジェクトが返されます。部分的または完全に隠されているものも含めて、すべてのビジュアル オブジェクトを識別するには、ヒット テストの結果のコールバックを使用します。

HitTest メソッドの Point パラメーターとして渡す座標値は、ヒット テストの対象のビジュアル オブジェクトの座標空間に対して相対的である必要があります。 たとえば、入れ子になったビジュアル オブジェクトが親の座標空間の (100, 100) に定義されている場合、(0, 0) での子ビジュアルのヒット テストは、親の座標空間の (100, 100) でのヒット テストと同じになります。

次のコードでは、ヒット テストに使用するイベントをキャプチャするために使用される UIElement オブジェクトにマウス イベント ハンドラーを設定する方法を示します。

        ' Respond to the left mouse button down event by initiating the hit test.
        Private Overloads Sub OnMouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
            ' Retrieve the coordinate of the mouse position.
            Dim pt As Point = e.GetPosition(CType(sender, UIElement))

            ' Perform the hit test against a given portion of the visual object tree.
            Dim result As HitTestResult = VisualTreeHelper.HitTest(myCanvas, pt)

            If result IsNot Nothing Then
                ' Perform action on hit visual object.
            End If
        End Sub
// Respond to the left mouse button down event by initiating the hit test.
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Perform the hit test against a given portion of the visual object tree.
    HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);

    if (result != null)
    {
        // Perform action on hit visual object.
    }
}

ヒット テストに対するビジュアル ツリーの影響

ビジュアル ツリー内の開始点によって、ヒット テストによるオブジェクトの列挙時に返されるオブジェクトが決定されます。 ヒット テストの対象となるオブジェクトが複数存在する場合、ビジュアル ツリー内の開始点として使用されるビジュアル オブジェクトは、対象となるすべてのオブジェクトの共通の先祖である必要があります。 たとえば、次の図のボタン要素と描画ビジュアルの両方のヒット テストを行う場合、ビジュアル ツリー内の開始点をその両方の共通の先祖に設定する必要があります。 この場合、キャンバス要素がボタン要素と描画ビジュアルの両方の共通の先祖になります。

ビジュアル ツリー階層の図

ビジュアル ツリー階層のダイアグラム

メモメモ

IsHitTestVisible プロパティは、描画されたコンテンツの一部からヒット テスト結果として UIElement 派生オブジェクトが返される可能性があるかどうかを宣言する値を取得または設定します。これにより、ビジュアル ツリーを選択的に変更し、ヒット テストの対象となるビジュアル オブジェクトを決定することができます。

ヒット テスト結果のコールバックの使用

ジオメトリに指定した座標値が含まれるビジュアル ツリーのすべてのビジュアル オブジェクトを列挙できます。 これにより、他のビジュアル オブジェクトによって部分的または完全に隠されているものも含めて、すべてのビジュアル オブジェクトを識別することができます。 ビジュアル ツリーのビジュアル オブジェクトを列挙するには、HitTest メソッドとヒット テスト コールバック関数を使用します。 ヒット テスト コールバック関数は、指定した座標値がビジュアル オブジェクトに含まれる場合にシステムによって呼び出されます。

ヒット テストの結果の列挙時には、ビジュアル ツリーを変更する操作を実行しないでください。 走査中のビジュアル ツリーに対してオブジェクトの追加または削除を行うと、予測不可能な動作を招く可能性があります。 ビジュアル ツリーの変更は、HitTest メソッドが終了した後に安全に実行できます。 ArrayList などのデータ構造体を提供して、ヒット テストの結果の列挙時に値を格納することもできます。

        ' Respond to the right mouse button down event by setting up a hit test results callback.
        Private Overloads Sub OnMouseRightButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
            ' Retrieve the coordinate of the mouse position.
            Dim pt As Point = e.GetPosition(CType(sender, UIElement))

            ' Clear the contents of the list used for hit test results.
            hitResultsList.Clear()

            ' Set up a callback to receive the hit test result enumeration.
            VisualTreeHelper.HitTest(myCanvas, Nothing, New HitTestResultCallback(AddressOf MyHitTestResult), New PointHitTestParameters(pt))

            ' Perform actions on the hit test results list.
            If hitResultsList.Count > 0 Then
                Console.WriteLine("Number of Visuals Hit: " & hitResultsList.Count)
            End If
        End Sub
// Respond to the right mouse button down event by setting up a hit test results callback.
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas, null,
        new HitTestResultCallback(MyHitTestResult),
        new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        Console.WriteLine("Number of Visuals Hit: " + hitResultsList.Count);
    }
}

ヒット テストのコールバック メソッドは、バーチャル ツリーの特定のビジュアル オブジェクトでヒット テストが識別されたときに、ユーザーが実行するアクションを定義します。 アクションを実行したら、引き続きその他のビジュアル オブジェクトを列挙するかどうかを決定する HitTestResultBehavior 値を返します。

        ' Return the result of the hit test to the callback.
        Public Function MyHitTestResult(ByVal result As HitTestResult) As HitTestResultBehavior
            ' Add the hit test result to the list that will be processed after the enumeration.
            hitResultsList.Add(result.VisualHit)

            ' Set the behavior to return visuals at all z-order levels.
            Return HitTestResultBehavior.Continue
        End Function
// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
    // Add the hit test result to the list that will be processed after the enumeration.
    hitResultsList.Add(result.VisualHit);

    // Set the behavior to return visuals at all z-order levels.
    return HitTestResultBehavior.Continue;
}
メモメモ

ヒットしたビジュアル オブジェクトの列挙の順序は、z オーダー順です。z オーダーの最上位のビジュアル オブジェクトが最初に列挙されます。その他のビジュアル オブジェクトは、z オーダーの上位から順に列挙されます。この列挙の順序は、ビジュアルのレンダリング順序に対応します。

Stop を返すことによって、ヒット テスト コールバック関数の任意の時点でビジュアル オブジェクトの列挙を停止できます。

            ' Set the behavior to stop enumerating visuals.
            Return HitTestResultBehavior.Stop
// Set the behavior to stop enumerating visuals.
return HitTestResultBehavior.Stop;

ヒット テスト フィルターのコールバックの使用

オプションのヒット テスト フィルターを使用して、ヒット テストの結果に渡されるオブジェクトを制限できます。 これにより、ヒット テストの結果でビジュアル ツリーの一部を処理する必要がない場合、その部分を無視できます。 ヒット テスト フィルターを実装するには、ヒット テスト フィルターのコールバック関数を定義して、HitTest メソッドを呼び出すときにパラメーター値として渡します。

        ' Respond to the mouse wheel event by setting up a hit test filter and results enumeration.
        Private Overloads Sub OnMouseWheel(ByVal sender As Object, ByVal e As MouseWheelEventArgs)
            ' Retrieve the coordinate of the mouse position.
            Dim pt As Point = e.GetPosition(CType(sender, UIElement))

            ' Clear the contents of the list used for hit test results.
            hitResultsList.Clear()

            ' Set up a callback to receive the hit test result enumeration.
            VisualTreeHelper.HitTest(myCanvas, New HitTestFilterCallback(AddressOf MyHitTestFilter), New HitTestResultCallback(AddressOf MyHitTestResult), New PointHitTestParameters(pt))

            ' Perform actions on the hit test results list.
            If hitResultsList.Count > 0 Then
                ProcessHitTestResultsList()
            End If
        End Sub
// Respond to the mouse wheel event by setting up a hit test filter and results enumeration.
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas,
                      new HitTestFilterCallback(MyHitTestFilter),
                      new HitTestResultCallback(MyHitTestResult),
                      new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}

オプションのヒット テスト フィルターのコールバック関数を指定しない場合は、HitTest メソッドに対してパラメーターとして null 値を渡します。

            ' Set up a callback to receive the hit test result enumeration,
            ' but no hit test filter enumeration.
            VisualTreeHelper.HitTest(myCanvas, Nothing, New HitTestResultCallback(AddressOf MyHitTestResult), New PointHitTestParameters(pt)) ' No hit test filtering.
// Set up a callback to receive the hit test result enumeration,
// but no hit test filter enumeration.
VisualTreeHelper.HitTest(myCanvas,
                  null,  // No hit test filtering.
                  new HitTestResultCallback(MyHitTestResult),
                  new PointHitTestParameters(pt));

ビジュアル ツリーから余分なものを取り除く

ヒット テスト フィルターを使用したビジュアル ツリーの簡略化

ヒット テスト フィルターのコールバック関数を使用すると、レンダリングされるコンテンツに指定した座標が含まれるすべてのビジュアルを列挙できます。 ただし、ヒット テストの結果のコールバック関数で、ビジュアル ツリーの一部の分岐を処理する必要がない場合、これらの分岐を無視できます。 ヒット テスト フィルターのコールバック関数の戻り値によって、ビジュアル オブジェクトの列挙体が実行するアクションの種類が決定されます。 たとえば、値 ContinueSkipSelfAndChildren を返した場合、ヒット テストの結果の列挙体から現在のビジュアル オブジェクトおよびその子を削除できます。 つまり、ヒット テストの結果のコールバック関数は、列挙体でこれらのオブジェクトを認識しなくなります。 オブジェクトのビジュアル ツリーから余分なものを取り除くと、ヒット テストの結果の列挙体が渡されるときの処理を減らすことができます。 次のコード例では、フィルターはラベルとその子孫をスキップし、他のすべてのヒット テストを行います。

        ' Filter the hit test values for each object in the enumeration.
        Public Function MyHitTestFilter(ByVal o As DependencyObject) As HitTestFilterBehavior
            ' Test for the object value you want to filter.
            If o.GetType() Is GetType(Label) Then
                ' Visual object and descendants are NOT part of hit test results enumeration.
                Return HitTestFilterBehavior.ContinueSkipSelfAndChildren
            Else
                ' Visual object is part of hit test results enumeration.
                Return HitTestFilterBehavior.Continue
            End If
        End Function
// Filter the hit test values for each object in the enumeration.
public HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
{
    // Test for the object value you want to filter.
    if (o.GetType() == typeof(Label))
    {
        // Visual object and descendants are NOT part of hit test results enumeration.
        return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
    }
    else
    {
        // Visual object is part of hit test results enumeration.
        return HitTestFilterBehavior.Continue;
    }
}
メモメモ

ヒット テスト フィルターのコールバックは、ヒット テストの結果のコールバックが呼び出されない場合に呼び出されることがあります。

既定のヒット テストのオーバーライド

HitTestCore メソッドをオーバーライドすることにより、ビジュアル オブジェクトの既定のヒット テストのサポートをオーバーライドできます。 つまり、HitTest メソッドを呼び出すと、HitTestCore のオーバーライドされた実装が呼び出されます。 座標がビジュアル オブジェクトのレンダリングされるコンテンツの外側にあっても、ビジュアル オブジェクトの外接する四角形内でヒット テストが実行されると、オーバーライドされたメソッドが呼び出されます。

        ' Override default hit test support in visual object.
        Protected Overrides Overloads Function HitTestCore(ByVal hitTestParameters As PointHitTestParameters) As HitTestResult
            Dim pt As Point = hitTestParameters.HitPoint

            ' Perform custom actions during the hit test processing,
            ' which may include verifying that the point actually
            ' falls within the rendered content of the visual.

            ' Return hit on bounding rectangle of visual object.
            Return New PointHitTestResult(Me, pt)
        End Function
// Override default hit test support in visual object.
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
    Point pt = hitTestParameters.HitPoint;

    // Perform custom actions during the hit test processing,
    // which may include verifying that the point actually
    // falls within the rendered content of the visual.

    // Return hit on bounding rectangle of visual object.
    return new PointHitTestResult(this, pt);
}

ビジュアル オブジェクトの外接する四角形とレンダリングされるコンテンツの両方に対してヒット テストを行う必要が生じる場合もあります。 オーバーライドされた HitTestCore メソッドで PointHitTestParameters パラメーター値を基本メソッド HitTestCore に対するパラメーターとして使用すると、ビジュアル オブジェクトの外接する四角形のヒットに基づいてアクションを実行し、ビジュアル オブジェクトのレンダリングされるコンテンツに対する 2 番目のヒット テストを実行することができます。

        ' Override default hit test support in visual object.
        Protected Overrides Overloads Function HitTestCore(ByVal hitTestParameters As PointHitTestParameters) As HitTestResult
            ' Perform actions based on hit test of bounding rectangle.
            ' ...

            ' Return results of base class hit testing,
            ' which only returns hit on the geometry of visual objects.
            Return MyBase.HitTestCore(hitTestParameters)
        End Function
// Override default hit test support in visual object.
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
    // Perform actions based on hit test of bounding rectangle.
    // ...

    // Return results of base class hit testing,
    // which only returns hit on the geometry of visual objects.
    return base.HitTestCore(hitTestParameters);
}

参照

処理手順

方法 : ビジュアル内のジオメトリのヒット テストを実行する

方法 : Win32 ホスト コンテナーを使用してヒット テストを実行する

参照

HitTest

HitTestResult

HitTestResultCallback

HitTestFilterCallback

IsHitTestVisible

その他の技術情報

DrawingVisuals を使用したヒット テストのサンプル

Win32 相互運用によるヒット テストのサンプル