Share via


視覺分層中的點擊測試

本主題提供視覺分層所提供點擊測試功能的概觀。 點擊測試支援可讓您判斷幾何或點值是否落在 轉譯的內容 Visual 中,讓您實作使用者介面行為,例如選取矩形來選取多個物件。

點擊測試案例

類別 UIElement 會提供 InputHitTest 方法,可讓您使用指定的座標值對專案進行點擊測試。 在許多情況下, InputHitTest 方法會提供實作元素點擊測試所需的功能。 不過,有幾個案例,您可能需要在視覺分層實作點擊測試。

  • 針對非 UIElement 物件進行點擊測試:如果您點擊測試非 UIElement 物件,例如 DrawingVisual 或 繪圖物件,就會套用這項測試。

  • 使用幾何進行點擊測試︰這適用於您需要使用幾何物件,而不是點的座標值進行點擊測試時。

  • 對多個物件進行點擊測試︰這適用於當您需要對多個物件進行點擊測試,例如重疊的物件。 您可以取得和幾何或點交集的所有視覺效果結果,不只有第一個結果。

  • UIElement忽略點擊測試原則:當您需要忽略 UIElement 點擊測試原則時,就會套用這項原則,這會考慮下列因素,例如是否停用或隱藏專案。

注意

如需示範在視覺分層進行點擊測試的完整程式碼範例,請參閱使用 DrawingVisuals 進行點擊測試範例 (英文)使用 Win32 交互操作進行點擊測試範例 (英文)

點擊測試支援

類別中 VisualTreeHelper 方法的目的是 HitTest 判斷幾何或點座標值是否在指定物件的轉譯內容中,例如控制項或圖形專案。 例如,您可以使用點擊測試來判斷物件的週框矩形內的滑鼠點擊是否落於圓形的幾何內。 您也可以選擇覆寫預設點擊測試實作,以執行您的自訂點擊測試計算。

下圖說明非矩形物件的區域和其週框之間的關聯性。

Diagram of valid hit test region
有效點擊測試區域的圖表

點擊測試和疊置順序

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

在下圖中,圓形物件位於正方形和三角形物件上方。 如果您只想要點擊測試其迭置順序值為最上層的視覺物件,您可以將視覺點擊測試列舉設定為從 HitTestResultCallback 傳回 Stop ,以在第一個專案之後停止點擊測試周遊。

Diagram of the z-order of a visual tree
視覺化樹狀之疊置順序的圖表

如果您想要列舉特定點或幾何下的所有視覺物件,請從 HitTestResultCallback 傳回 Continue 。 這表示您可以對其他物件底下的視覺物件進行點擊測試,即使它們完全遭到遮蔽。 如需詳細資訊,請參閱「使用點擊測試結果回呼 」一節中的範例程式碼。

注意

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

使用預設點擊測試

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

注意

預設點擊測試一定會傳回疊置順序中最上層的物件。 若要識別所有視覺物件,甚至是遭到部分或全部遮蔽的物件,可使用點擊測試結果回呼。

您傳遞做為方法之 point 參數 HitTest 的座標值,必須相對於您所點擊測試之視覺物件的座標空間。 例如,如果您在父項座標空間的 (100, 100) 定義巢狀視覺物件,然後對位於 (0, 0) 的子視覺物件進行點擊測試,其相當於父項座標空間的 (100, 100)。

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

// 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.
    }
}
' 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

視覺化樹狀結構如何影響點擊測試

視覺化樹狀結構中的起點可決定在物件的點擊測試列舉期間會傳回哪些物件。 如果您有多個想要進行點擊測試的物件,在視覺化樹狀結構中做為起點的視覺物件必須是所有相關物件的通用上階。 例如,如果您想要對下圖中的按鈕元素和繪圖視覺物件進行點擊測試,您必須將視覺樹狀結構中的起點設定為兩者的通用上階。 在此情況下,畫布元素是按鈕元素和繪製視覺物件兩者的通用上階。

Diagram of a visual tree hierarchy
視覺化樹狀階層架構的圖表

注意

屬性 IsHitTestVisible 會取得或設定值,這個值會宣告衍生物件是否可以 UIElement 從其轉譯內容的某些部分傳回作為點擊測試結果。 這可讓您選擇性地更改視覺化樹狀結構,以判斷哪一個視覺物件要進行點擊測試。

使用點擊測試結果回呼

視覺化樹狀結構中的幾何只要包含指定座標值,您就可以列舉所有視覺物件。 這可讓您識別所有視覺物件,甚至是遭到其他視覺物件部分或全部遮蔽的那些物件。 若要列舉視覺化樹狀結構中的視覺物件, HitTest 請使用 方法搭配點擊測試回呼函式。 當視覺物件中包含您指定的座標值時,系統便會呼叫點擊測試回呼函式。

在點擊測試結果列舉期間,您不應該執行任何修改視覺化樹狀結構的作業。 在周遊時新增或移除視覺化樹狀結構的物件,可能會導致無法預期的行為。 您可以在方法傳回之後 HitTest 安全地修改視覺化樹狀結構。 您可能想要提供資料結構,例如 ArrayList ,以在點擊測試結果列舉期間儲存值。

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

點擊測試回呼方法定義您在視覺化樹狀結構中的特定視覺物件上,識別出點擊測試時執行的動作。 執行動作之後,您會傳回值 HitTestResultBehavior ,判斷是否要繼續列舉任何其他視覺物件。

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

注意

點擊視覺物件列舉順序是依照疊置順序。 在最上層疊置順序層級的視覺物件是第一個列舉的物件。 任何其他視覺物件都會以遞減的疊置順序層級列舉。 此列舉類型順序對應至視覺效果的轉譯順序。

您可以傳回 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 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();
    }
}
' 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

如果您不想提供選擇性點擊測試篩選回呼函式,請傳遞 null 值作為方法的參數 HitTest

// 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));
' 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.

Pruning a visual tree using a hit test filter
剪除視覺化樹狀結構

點擊測試篩選回呼函式可讓您列舉呈現內容中包含指定座標的所有視覺效果。 不過,您可能想要忽略點擊測試結果回呼函式中處理時不感興趣的視覺化樹狀結構特定分支。 點擊測試篩選回呼函式的傳回值會決定列舉視覺物件時應採取的動作類型。 例如,如果您傳回 值, ContinueSkipSelfAndChildren 則可以從點擊測試結果列舉中移除目前的視覺物件及其子系。 這表示點擊測試結果回呼函式不會在其列舉中看到這些物件。 剪除物件的視覺化樹狀結構會減少在點擊測試結果列舉通過期間的處理量。 在下列程式碼範例中,篩選會略過標籤和其下階並點擊測試所有其他項目。

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

注意

有時候會呼叫點擊測試篩選回呼,以免未呼叫點擊測試結果回呼。

覆寫預設點擊測試

您可以覆 HitTestCore 寫 方法,覆寫視覺效果物件的預設點擊測試支援。 這表示當您叫 HitTest 用 方法時,會呼叫 覆寫的 HitTestCore 實作。 即使座標落在視覺物件的呈現內容外部,還是會在點擊測試落在視覺物件的週框內時呼叫覆寫方法。

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

有時候您可能想要對週框和視覺物件的呈現內容進行點擊測試。 藉由使用 PointHitTestParameters 覆寫 HitTestCore 方法中的參數值做為基底方法 HitTestCore 的參數,您可以根據視覺物件的周框點擊來執行動作,然後針對視覺物件的轉譯內容執行第二次點擊測試。

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

另請參閱