locked
Is VisualTreeHelper.HitTest() supposed to pay attention to IsHitTestVisible? RRS feed

  • Question

  • I have the following code, and the assertion hits:

          HitTestResult htr = VisualTreeHelper.HitTest(canvas, e.GetPosition(canvas));
          Shape r = htr.VisualHit as Shape;
          Assert(r.IsHitTestVisible == true); // this fails

    What this means is that a shape that I'm adding, that I have explicitly set to IsHitTestVisible=false, is being "hit" by VisualTreeHelper.HitTest().

    Reading the documentation, it simply says that something with "IsHitTestVisible = false" should not participate in "any kind of hit testing." The documentation for VisualTreeHelper.HitTest() doesn't talk about any interaction with the IsHitTestVisible property at all, one way or another.

    Is it the case that I have to use a custom hit-test filter that checks this property on each Visual, in order to not "hit" visuals with IsHitTestVisible == false? If so, is that documented somewhere, or just part of stuff you're supposed to know through osmosis?


    XNA/DirectX MVP 2006-present
    Monday, February 9, 2009 6:11 AM

Answers

  • I debugged throught the responsible code, and you are right the check is missing. The method "internal HitTestResultBehavior HitTestPoint(HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback,PointHitTestParameters pointParams)" loops through it's visual children, but doesn't check this property:

                        for (int i=childCount-1; i>=0; i--)   
                        {  
                            Visual child = GetVisualChild(i);  
                            if (child != null)  
                            {  

    Might be "by design" to find even "invisible" elements, but imho seems to be a bug. The HitTest method provides a callback which you could use to bypass this issue.

    I check with .NET reflector the usage of this property, and I only found some checks for mouse capture, and input checks, ...
    • Marked as answer by Hua Chen Friday, February 13, 2009 6:00 AM
    Monday, February 9, 2009 8:11 AM

All replies

  • I debugged throught the responsible code, and you are right the check is missing. The method "internal HitTestResultBehavior HitTestPoint(HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback,PointHitTestParameters pointParams)" loops through it's visual children, but doesn't check this property:

                        for (int i=childCount-1; i>=0; i--)   
                        {  
                            Visual child = GetVisualChild(i);  
                            if (child != null)  
                            {  

    Might be "by design" to find even "invisible" elements, but imho seems to be a bug. The HitTest method provides a callback which you could use to bypass this issue.

    I check with .NET reflector the usage of this property, and I only found some checks for mouse capture, and input checks, ...
    • Marked as answer by Hua Chen Friday, February 13, 2009 6:00 AM
    Monday, February 9, 2009 8:11 AM
  • Monday, February 9, 2009 6:55 PM
  • I have created a (static) VisualTreeHelperExt class, that contains the following function that does work correctly:

    public static HitTestResult HitTest(Visual visual, Point point)
    {
      // This 'HitTest' method also takes the 'IsHitTestVisible' and 'IsVisible' properties
      // into account, so use it instead of the normal VisualTreeHelper.HitTest instead!
      HitTestResult result = null;
    
      // Use the advanced HitTest method and specify a custom filter that filters out the
      // invisible elements or the elements that don't allow hittesting.
      VisualTreeHelper.HitTest(visual,
                   (target) => {
                     var uiElement = target as UIElement;
                     if ((uiElement != null) && (!uiElement.IsHitTestVisible || !uiElement.IsVisible))
                       return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
                     else
                       return HitTestFilterBehavior.Continue;
                   },
                   (target) => { 
                     result = target; 
                     return HitTestResultBehavior.Stop; 
                   }, 
                   new PointHitTestParameters(point));
          
      // Return the result
      return result;
    }
    
    

    Hope this helps...
    Thursday, June 9, 2011 12:14 PM