共用方式為


視覺分層中的點擊測試

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

點擊測試案例

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

  • 對非 UIElement 物件進行點擊測試:如果您要對非 UIElement 物件進行點擊測試,例如 DrawingVisual 或圖形物件,則可套用此方法。

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

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

  • 忽略 UIElement 點擊測試原則︰當您需要忽略 UIElement 點擊測試原則時可套用此方法,點擊測試原則會考慮元素是否已停用或隱藏等因素。

注意

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

點擊測試支援

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

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

有效點擊測試區域的圖表
有效點擊測試區域的圖表

點擊測試和疊置順序

Windows Presentation Foundation (WPF) 視覺分層支援對點或幾何底下的所有物件進行點擊測試,而不是只有最上層物件。 以疊置順序傳回結果。 不過,您當做參數傳送給 HitTest 方法的視覺物件可判斷將進行點擊測試的視覺化樹狀結構部分。 您可以對整個視覺化樹狀結構或其任何部分進行點擊測試。

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

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

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

注意

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

使用預設點擊測試

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

注意

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

您當做 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

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

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

視覺化樹狀階層架構的圖表
視覺化樹狀階層架構的圖表

注意

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.

使用點擊測試篩選剪除視覺化樹狀
剪除視覺化樹狀結構

點擊測試篩選回呼函式可讓您列舉呈現內容中包含指定座標的所有視覺效果。 不過,您可能想要忽略點擊測試結果回呼函式中處理時不感興趣的視覺化樹狀結構特定分支。 點擊測試篩選回呼函式的傳回值會決定列舉視覺物件時應採取的動作類型。 例如,如果您傳回值 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

有時候您可能想要對週框和視覺物件的呈現內容進行點擊測試。 藉由在覆寫 HitTestCore 方法中使用 PointHitTestParameters 參數值作為基底方法 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

另請參閱