views:

35

answers:

1

I'm trying to detect the control under the mouse cursor, regardless of whether the control is enabled or not.

VisualTreeHelper.FindElementsInHostCoordinates ignores controls that have their IsEnabled property set to false. Is there any way to change this behavior, or any other way to find the controls at a specific screen location?

Thanks.

+1  A: 

You could implement your own recursive method to search the subtree and transform each element to the application's root visual in order to get its "absolute" bounds and then test to see if the "absolute" mouse point is within that region.

This might not be exactly what you need but should get you started. I basically made a replacement FindElementsInHostCoordinates with the same signature so it can be used in the same manner in your MouseMove handler. This method only tries to "hit test" FrameworkElements since it needs to know the ActualWidth and ActualHeight to calculate the hit region.

private IEnumerable<UIElement> FindAllElementsInHostCoordinates(Point intersectingPoint, UIElement subTree)
{
    var results = new List<UIElement>();

    int count = VisualTreeHelper.GetChildrenCount(subTree);

    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(subTree, i) as FrameworkElement;

        if (child != null)
        {
            GeneralTransform gt = child.TransformToVisual(Application.Current.RootVisual as UIElement);
            Point offset = gt.Transform(new Point(0, 0));
            Rect elementBounds = new Rect(offset.X, offset.Y, child.ActualWidth, child.ActualHeight);

            if (IsInBounds(intersectingPoint, elementBounds))
            {
                results.Add(child as UIElement);
            }
        }

        results.AddRange(FindAllElementsInHostCoordinates(intersectingPoint, child));
    }

    return results;
}

private bool IsInBounds(Point point, Rect bounds)
{
    if (point.X > bounds.Left && point.X < bounds.Right &&
        point.Y < bounds.Bottom && point.Y > bounds.Top)
    {
        return true;
    }

    return false;
}

You would then just need to make sure the point you are passing in from the MouseMove handler is relative to Application.Current.RootVisual:

IEnumerable<UIElement> elements = FindAllElementsInHostCoordinates(e.GetPosition(Application.Current.RootVisual), this);
Dan Auclair
That sounds slow, but I'll try it. I'm concerned that I won't be able to tell when I could get away with a (presumably faster) call to FindElementsInHostCoordinates vs. using this technique.
Greg
Yeah, you are walking the entire visual tree on every mouse move event so I'm sure the performance is not great. I'm not sure what the internal implementation of the real FindElementsInHostCoordinates looks like but I'm sure there are optimizations that can be made. This is just the first thing that came to my head, maybe there is a better solution if performance is a big concern in your application.
Dan Auclair