共用方式為


視覺分層中的點擊測試

本主題概要說明視覺分層所提供的點擊測試 (Hit Testing) 功能。 點擊測試支援可讓您判斷幾何圖案或點值是否在 Visual 的呈現內容範圍內,使您可以實作使用者介面行為 (例如選取矩形) 來選取多個物件。

這個主題包含下列章節。

  • 點擊測試案例
  • 點擊測試支援
  • 點擊測試和疊置順序
  • 使用預設點擊測試
  • 使用點擊測試結果回呼
  • 使用點擊測試篩選回呼
  • 覆寫預設點擊測試
  • 相關主題

點擊測試案例

UIElement 類別提供 InputHitTest 方法,使您可以使用指定的座標值對項目進行點擊測試。 在許多情況下,InputHitTest 方法都可提供實作項目點擊測試所需的功能。 不過,在某些案例中,您可能必須在視覺分層上實作點擊測試。

  • 對非 UIElement 物件執行點擊測試:如果您對非 UIElement 物件 (例如 DrawingVisual 或圖形物件) 進行點擊測試,便適用這種情況。

  • 使用幾何進行點擊測試:如果您需要使用幾何物件而非點座標值進行點擊測試,便適用這種情況。

  • 對多個物件執行點擊測試:當您需要對多個物件 (例如重疊的物件) 進行點擊測試時,便適用這種情況。 您可以取得與幾何或點交集之所有視覺項目的結果,而不只是第一個視覺項目。

  • 忽略 UIElement 點擊測試原則:當您需要忽略 UIElement 點擊測試原則時,便適用這種情況;此原則會考量項目是否已停用或不可見之類的因素。

注意事項注意事項

如需說明視覺分層點擊測試的完整程式碼範例,請參閱使用 DrawingVisual 進行點擊測試範例 (英文) 和使用 Win32 互通性進行點擊測試範例 (英文)。

點擊測試支援

VisualTreeHelper 類別中,HitTest 方法的目的是要判斷幾何或點座標值是否在指定之物件 (如控制項或圖形項目) 的呈現內容範圍內。 例如,您可以使用點擊測試判斷,在物件的週框 (Bounding Rectangle) 內按一下滑鼠是否會落在圓形的幾何範圍內。 您也可以選擇覆寫點擊測試的預設實作,以執行自己的點擊測試計算。

下圖顯示非矩形物件之區域與其週框之間的關係。

有效點擊測試區域的圖表

有效點擊測試區域的圖表

點擊測試和疊置順序

Windows Presentation Foundation (WPF) 視覺分層支援點或幾何圖形下所有物件的點擊測試,而不只是最上層的物件。 結果是按疊置順序 (Z-order) 傳回。 不過,您當做 HitTest 方法參數傳遞的視覺物件,則決定了視覺化樹狀結構的哪個部分要進行點擊測試。 您可以對整個視覺化樹狀結構或其任何部分進行點擊測試。

在下圖中,圓形物件位於方形和三角形兩個物件上面。 如果您只想對 z-order 值在最上面的視覺物件進行點擊測試,可以將視覺化點擊測試列舉型別設定成從 HitTestResultCallback 傳回 Stop,以便在第一個項目完成之後停止點擊測試周遊。

視覺化樹狀結構疊置順序的圖表

視覺化樹狀結構之疊置順序的圖表

如果要列舉特定點或幾何圖案底下的所有視覺物件,請從 HitTestResultCallback 傳回 Continue。 這表示您可以針對其他物件下面的視覺物件進行點擊測試,即使其他物件完全遮蔽了這些物件也沒有關係。 如需詳細資訊,請參閱<使用點擊測試結果回呼>一節中的範例程式碼。

注意事項注意事項

透明的視覺物件也可以進行點擊測試。

使用預設點擊測試

您可以使用 HitTest 方法指定要測試的視覺物件和點座標值,以識別某個點是否在視覺物件的幾何範圍內。 視覺物件參數可識別視覺化樹狀結構中要進行點擊測試搜尋的起點。 如果在視覺化樹狀結構中找到視覺物件,而物件的幾何包含座標,便會將該視覺物件設定成 HitTestResult 物件的 VisualHit 屬性。 接著再從 HitTest 方法傳回 HitTestResult。 如果該點未包含在點擊測試的視覺化子樹狀結構中,HitTest 便會傳回 null。

注意事項注意事項

預設點擊測試一定會依疊置順序傳回最上層的物件。若要識別所有的視覺物件,包括可能有一部分或全部被遮住的視覺物件,請使用點擊測試結果回呼 (Callback)。

做為 HitTest 方法參數傳遞的座標值,必須相對於您要進行點擊測試之視覺物件的座標空間 (Coordinate Space)。 例如,如果您將巢狀視覺物件定義在父代 (Parent) 之座標空間中的 (100, 100),則對位於 (0, 0) 的子視覺項目進行點擊測試,相當於在父代座標空間的 (100, 100) 進行點擊測試。

下列程式碼示範如何設定 UIElement 物件的滑鼠事件處理常式 (Event Handler),以擷取用於點測擊測試的事件。

        ' 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;
}
注意事項注意事項

點擊視覺物件的列舉順序是採用疊置順序。位於疊置順序最上層的視覺物件是列舉的第一個物件。列舉的其他所有視覺物件則按遞減疊置順序層級排列。這個列舉順序對應於視覺化項目的呈現順序。

您可以透過傳回 Stop,隨時在點擊測試回呼函式中停止列舉視覺物件。

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

使用點擊測試篩選回呼

您可以使用選擇性 (Optional) 的點擊測試篩選條件,限制傳遞至點擊測試結果的物件。 這可讓您在處理點擊測試結果時,忽略自己不感興趣的視覺化樹狀結構部分。 若要實作點擊測試篩選條件,您可以定義點擊測試篩選回呼函式,並在呼叫 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,您可以從點擊測試結果列舉中移除目前的視覺物件及其子系。 這表示點擊測試結果回呼函式不會在其列舉中看到這些物件。 清除物件的視覺化樹狀結構可減少點擊測試列舉傳遞期間的處理量。 在下列程式碼範例中,篩選條件會略過標籤 (Label) 及其子代,並對其餘所有項目進行點擊測試。

        ' 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 方法,覆寫視覺物件的預設點擊測試支援。 這表示當您叫用 (Invoke) 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 的參數,您可以執行以視覺物件週框點擊做為基礎的動作,然後再對視覺物件的呈現內容執行第二次點擊測試。

        ' 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);
}

請參閱

工作

HOW TO:對 Visual 中的幾何進行點擊測試

HOW TO:使用 Win32 裝載容器進行點擊測試

參考

HitTest

HitTestResult

HitTestResultCallback

HitTestFilterCallback

IsHitTestVisible

其他資源

使用 DrawingVisuals 進行點擊測試範例

使用 Win32 互通性進行點擊測試範例