2010-09-19 122 views
2

我有以下的用户控件:点和它的名字:WPF用户控件的HitTest

<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"> 

    <StackPanel> 
     <Ellipse Stroke="DarkBlue" Fill="LightBlue" Height="10" Width="10"/> 
     <TextBlock Text="Eiffel Tower"/>   
    </StackPanel> 
</UserControl> 

这是很酷的。

现在,我有一个面板,在女巫我需要休养生息我StopPoints我击中鼠标:

public partial class StopsPanel : UserControl 
{ 
    private List<StopPoint> hitList = new List<StopPoint>(); 
    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 > 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; 
     } 
    } 
} 

所以,你可以看到,的的HitTest从来没有问题识别的用户控件(StopPoint),而是其组件TextBlock,椭圆或甚至边界)。
当我将业务对象关联到StopPoint元素时,我需要在MouseHitting时获取它,而不是它的组成元素。

有没有办法做到这一点?

编辑:

使用过滤器(现在,它不会在所有的HitTestCallback进入):

using System.Collections.Generic; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 
using System.Windows.Media; 

namespace ShapeTester 
{ 
    /// <summary> 
    /// Interaction logic for StopsPanel.xaml 
    /// </summary> 
    public partial class StopsPanel : UserControl 
    { 
     private List<StopPoint> hitList = new List<StopPoint>(); 
     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 > 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; 
      } 
     } 
    } 
} 
+0

您是否尝试添加HitTestFilterCallback并返回ContinueSkipChildren(如果它在StopPoint上)?我看到你当前传递null作为过滤器回调。 – Bubblewrap 2010-09-19 16:33:53

+0

@Bubblewrap:嗯...... e ...你是什么意思? – serhio 2010-09-19 17:13:10

+0

VisualTreeHelper.HitTest的第二个参数,你可以指定一个HitTestFilterCallback。请参阅:http://msdn.microsoft.com/en-us/library/ms752097。aspx#using_a_hit_test_filter_callback – Bubblewrap 2010-09-19 17:54:04

回答

0

你能不能添加鼠标点击事件侦听器来分,只投发件人到StopPoint,这将是一切好?不需要额外的命中测试代码。

+0

我不确定...我需要通过拖动来移动它... – serhio 2010-09-19 17:18:18

3

您可以使用VisualTreeHelper寻父停止点:

var element = result.VisualHit; 
while(element != null && !(element is StopPoint)) 
    element = VisualTreeHelper.GetParent(element); 

if(element == null) return; 
3

我想写一个解释,但我已经找到了一个体面的一个:

https://stackoverflow.com/a/7162443/717732

点是:

您的UserControl.HitTestCore()留给probaly返回NULL的默认实现,这会导致UC将被跳过而不是传递给resultCallback。

默认行为不是错误。这是一个不明显的,聪明的设计 - 总而言之,你的控制没有视觉效果,它只是一些有形状的孩子的容器,所以一般来说,UC没有可攻击性和杂乱的步行路径。你可能会认为它是一个缺点,因为你的代码的简洁性可以从UC的可测性中受益。然而,这里的目标并不简单 - 这是速度。实际上这是一个重要的特征,因为它真的减少了树木行者必须执行实际交叉点的物品数量!

所以 - 无论是执行HitTestCore并返回一些非空,或则hitTest为用户控件的孩子,而不是,再有一个正确的结果时,但等于其子,使用VisualTreeHelper.GetParent直到你走高达用户控件你通缉。