2010-09-19 83 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直到你走高達用戶控件你通緝。