title: WPF UserControl HitTest
body: <p>I have the following user control:  a dot and its name: </p>
  
  <pre><code>&lt;UserControl x:Class="ShapeTester.StopPoint"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       mc:Ignorable="d" 
       d:DesignHeight="25" d:DesignWidth="100"&gt;
  
     &lt;StackPanel&gt;
        &lt;Ellipse Stroke="DarkBlue" Fill="LightBlue" Height="10" Width="10"/&gt;
        &lt;TextBlock Text="Eiffel Tower"/&gt;        
    &lt;/StackPanel&gt;
  &lt;/UserControl&gt;
  </code></pre>
  
  <p>This is cool. </p>
  
  <p>Now, I have a panel, in witch I need to recuperate my StopPoints that I hit with the Mouse: </p>
  
  <pre><code>public partial class StopsPanel : UserControl
  {
      private List&lt;StopPoint&gt; hitList = new List&lt;StopPoint&gt;();
      private EllipseGeometry hitArea = new EllipseGeometry();
  
      public StopsPanel()
      {
          InitializeComponent();
          Initialize();
      }
  
      private void Initialize()
      {
          foreach (StopPoint point in StopsCanvas.Children)
          {
              point.Background = Brushes.LightBlue;
          }
      }
  
      private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      {
          // Initialization:
          Initialize();
          // Get mouse click point:
          Point pt = e.GetPosition(StopsCanvas);
          // Define hit-testing area:
          hitArea = new EllipseGeometry(pt, 1.0, 1.0);
          hitList.Clear();
          // Call HitTest method:
          VisualTreeHelper.HitTest(StopsCanvas, null,
          new HitTestResultCallback(HitTestCallback),
          new GeometryHitTestParameters(hitArea));
          if (hitList.Count &gt; 0)
          {
              foreach (StopPoint point in hitList)
              {
                  // Change rectangle fill color if it is hit:
                  point.Background = Brushes.LightCoral;
              }
              MessageBox.Show(string.Format(
                  "You hit {0} StopPoint(s)", hitList.Count));
          }
      }
  
      public HitTestResultBehavior HitTestCallback(HitTestResult result)
      {
          if (result.VisualHit is StopPoint)
          {
              //
              //-------- NEVER ENTER HERE!!! :(
              //
  
              // Retrieve the results of the hit test.
              IntersectionDetail intersectionDetail =
              ((GeometryHitTestResult)result).IntersectionDetail;
              switch (intersectionDetail)
              {
                  case IntersectionDetail.FullyContains:
                  // Add the hit test result to the list:
                      hitList.Add((StopPoint)result.VisualHit);
                      return HitTestResultBehavior.Continue;
                  case IntersectionDetail.Intersects:
                  // Set the behavior to return visuals at all z-order levels:
                      return HitTestResultBehavior.Continue;
                  case IntersectionDetail.FullyInside:
                  // Set the behavior to return visuals at all z-order levels:
                      return HitTestResultBehavior.Continue;
                  default:
                      return HitTestResultBehavior.Stop;
              }
          }
          else
          {
              return HitTestResultBehavior.Continue;
          }
      }
  }
  </code></pre>
  
  <p>So, as you can see, the problem that <strong>the HitTest never identifies an UserControl(StopPoint) as it is, but rather its components</strong>(<em>TextBlock</em>, <em>Ellipse</em> or even <em>Border</em>).<br>
  As I associate the business object to the StopPoint element, I need to obtain it when MouseHitting, and not its composing elements.</p>
  
  <p>Is there a way to do it?</p>
  
  <p>EDIT: </p>
  
  <p>Using filter (now, it does not enter at all in the HitTestCallback): </p>
  
  <pre><code>using System.Collections.Generic;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Input;
  using System.Windows.Media;
  
  namespace ShapeTester
  {
      /// &lt;summary&gt;
      /// Interaction logic for StopsPanel.xaml
      /// &lt;/summary&gt;
      public partial class StopsPanel : UserControl
      {
          private List&lt;StopPoint&gt; hitList = new List&lt;StopPoint&gt;();
          private EllipseGeometry hitArea = new EllipseGeometry();
  
          public StopsPanel()
          {
              InitializeComponent();
              Initialize();
          }
  
          private void Initialize()
          {
              foreach (StopPoint point in StopsCanvas.Children)
              {
                  point.Background = Brushes.LightBlue;
              }
          }
  
          private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
          {
              // Initialization:
              Initialize();
              // Get mouse click point:
              Point pt = e.GetPosition(StopsCanvas);
              // Define hit-testing area:
              hitArea = new EllipseGeometry(pt, 1.0, 1.0);
              hitList.Clear();
              // Call HitTest method:
              VisualTreeHelper.HitTest(StopsCanvas, 
                  new HitTestFilterCallback(MyHitTestFilter),
                  new HitTestResultCallback(HitTestCallback),
                  new GeometryHitTestParameters(hitArea));
  
              if (hitList.Count &gt; 0)
              {
                  foreach (StopPoint point in hitList)
                  {
                      // Change rectangle fill color if it is hit:
                      point.Background = Brushes.LightCoral;
                  }
                  MessageBox.Show(string.Format(
                      "You hit {0} StopPoint(s)", hitList.Count));
              }
          }
  
          public HitTestResultBehavior HitTestCallback(HitTestResult result)
          {
              if (result.VisualHit is StopPoint)
              {
                  //
                  //-------- NEVER ENTER HERE!!! :(
                  //
  
                  // Retrieve the results of the hit test.
                  IntersectionDetail intersectionDetail =
                  ((GeometryHitTestResult)result).IntersectionDetail;
                  switch (intersectionDetail)
                  {
                      case IntersectionDetail.FullyContains:
                      // Add the hit test result to the list:
                          hitList.Add((StopPoint)result.VisualHit);
                          return HitTestResultBehavior.Continue;
                      case IntersectionDetail.Intersects:
                      // Set the behavior to return visuals at all z-order levels:
                          return HitTestResultBehavior.Continue;
                      case IntersectionDetail.FullyInside:
                      // Set the behavior to return visuals at all z-order levels:
                          return HitTestResultBehavior.Continue;
                      default:
                          return HitTestResultBehavior.Stop;
                  }
              }
              else
              {
                  return HitTestResultBehavior.Continue;
              }
          }
  
          // 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(StopPoint))
              {
                  // Visual object's descendants are 
                  // NOT part of hit test results enumeration.
                  return HitTestFilterBehavior.ContinueSkipChildren;
              }
              else
              {
                  // Visual object is part of hit test results enumeration.
                  return HitTestFilterBehavior.Continue;
              }
          }
      }
  }
  </code></pre>