VisualTreeHelper.FindElementsInHostCoordinates 不 return 控制 IsHitTestVisible 设置为 false

VisualTreeHelper.FindElementsInHostCoordinates does not return controls with IsHitTestVisible set up to false

我正在做一些实验,我正在尝试收集所有放置在鼠标指针下的元素。

XAML测试代码

<StackPanel>
    <Button
        x:Name="HitButton"
        Content="Test Button" />
    <Button
        x:Name="NotHitButton"
        IsHitTestVisible="False"
        Content="Test Button" />
</StackPanel>

要获取鼠标指针的坐标,我使用 PointerMoved:

CoreWindow.GetForCurrentThread().PointerMoved += OnPointerMoved;

private void OnPointerMoved(CoreWindow sender, PointerEventArgs args)
{
    Point point = args.CurrentPoint.Position;
    IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(point, null, true);
    ...
}

这种方法有效,但是,当控件将 属性 IsHitTestVisible 设置为 false 时,即使我设置了第三个 FindElementsInHostCoordinates 也不会考虑它此方法的参数 includeAllElementstrue.

根据 documentation 我希望如果我将参数 includeAllElements 设置为 true,它应该找到所有元素,包括那些设置了 IsHitTestVisible 的控件至 false.

includeAllElements [System.Boolean]

true to include all elements that intersect, including those elements considered to be invisible to hit testing. false to find only visible, hit-testable elements. The default is false.

我对 FindElementsInHostCoordinates 工作方式的理解有误吗?如果是这样,有没有其他方法如何检索特定坐标处的所有控件,即使它们将 IsHitTestVisible 设置为 false

我认为这个问题的关键点是什么是 "elements considered to be invisible to hit testing"。

我认为当我们将 IsHitTestVisible 设置为 false 时,该元素不被认为是不可见的,它只是不可见的。而根据Hit testing and input events,满足以下条件的元素是hit-testable.

  • The element's Visibility property value is Visible.
  • The element's Background or Fill property value is not null. A null Brush value results in transparency and hit test invisibility. (To make an element transparent but also hit testable, use a Transparent brush instead of null.)
  • If the element is a control, its IsEnabled property value must be true.
  • The element must have actual dimensions in layout. An element where either ActualHeight and ActualWidth are 0 won't fire input events.

所以我认为当一个元素的 Visibility 属性 值是 Collapsed 或者它的 BackgroundFill 属性 值是 null 或其 IsEnabled 属性 值为 false 时,该元素被认为是不可见的。我做了一个简单的测试。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Border Name="Border" Width="200" Height="300" BorderThickness="1" BorderBrush="Black" Background="AliceBlue">
        <Grid Name="Grid" Width="100" Height="250">
            <Rectangle Name="CollapsedRectangle" Fill="Azure" Visibility="Collapsed" Width="100" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" />
            <Rectangle Name="NoFillRectangle" Width="100" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,100,0,0" />
            <Button Name="Button" IsEnabled="False" VerticalAlignment="Top" Margin="0,200,0,0">CLICK</Button>
        </Grid>
    </Border>
</Grid>

public MainPage()
{
    this.InitializeComponent();
    CoreWindow.GetForCurrentThread().PointerMoved += (s, e) =>
    {
        Point point = e.CurrentPoint.Position;
        IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(point, Border, false);

        foreach (var item in elements)
        {
            FrameworkElement feItem = item as FrameworkElement;
            System.Diagnostics.Debug.WriteLine(feItem.Name);
        }

        System.Diagnostics.Debug.WriteLine("-----------------------------------------");
    };
}

includeAllElements参数设置为false时,输出如

Border
-----------------------------------------
Border
-----------------------------------------
Border
-----------------------------------------

并且当includeAllElements参数设置为true时,输出类似于

Border
-----------------------------------------
Grid
Border
-----------------------------------------
...
NoFillRectangle
Grid
Border
-----------------------------------------
...
Grid
Border
-----------------------------------------
Border
-----------------------------------------

所以似乎includeAllElements参数只有在元素的BackgroundFill属性值为null时才会生效。不确定这是否是设计使然。 可能只有 BackgroundFill 属性 值为 null 的元素被认为是不可见的。

的观察基本正确。

XAML 命中测试基于以下理念:如果元素在屏幕上占据 space 并且“ 产生墨迹" 然后它命中测试。

主要问题是什么产生墨水:

  • 对于具有 Brush(绝大多数)的元素,任何 非空 笔刷都被认为是 产生墨水 ,即使画笔不产生可见像素。例如 a SolidColorBrush 颜色 "Transparent" 仍然产生墨水 只有空刷不出墨.

  • 有一些特殊元素如SwapChainPanelMediaElement没有刷子但可以仍然产生墨水.

  • 这里不考虑Opacity 属性

因此,只要满足上述条件之一,即使不透明度等于 0,元素 仍会产生墨水

这意味着当 includeAllElements 参数设置为 true 时,不产生墨水的元素 将被考虑用于命中测试。在这种情况下,只要元素满足空间要求(point/rect 与元素边界相交),那么它和它的祖先都包含在结果中。

顺便说一句,我们正在与内容团队合作以完善此文档以使其更加清晰。