2010-09-19 5 views
2

У меня есть следующий пользовательский элемент управления: точку и его название:WPF UserControl 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 никогда не идентифицирует UserControl (StopPoint) как есть, а его компоненты (TextBlock, Ellipse или даже Граница).
Поскольку я связываю бизнес-объект с элементом StopPoint, мне нужно его получить, когда MouseHitting, а не его составляющие элементы.

Есть ли способ сделать это?

EDIT:

Использование фильтра (сейчас, это не входит вообще в 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

+0

@Bubblewrap: hmm ... e ... что вы имеете в виду? .. – serhio

+0

Второй параметр VisualTreeHelper.HitTest, вы можете указать HitTestFilterCallback. См. Здесь: http://msdn.microsoft.com/en-us/library/ms752097.aspx # using_a_hit_test_filter_callback – Bubblewrap

ответ

0

Могли бы вы не добавить МЫШЬЮ события слушателей к точкам, и просто бросить отправителя до StopPoint, и все будет хорошо? Нет необходимости в дополнительном тестовом коде.

+0

Я не уверен ... Мне нужно будет переместить его, перетащив затем ... – serhio

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

Точка is:

Ваш UserControl.HitTestCore() оставлен до реализации по умолчанию, который probaly возвращает NULL, что это вызывает UC будет пропущен вместо того, чтобы быть переданным в resultCallback.

Поведение по умолчанию не является ошибкой. Это не очевидный, умный дизайн - в целом, у вашего управления нет визуальных эффектов, это только контейнер для некоторых детей, у которых есть фигуры, поэтому обычно нет смысла в UC быть hittestable и загромождать путь ходьбы. Вы можете видеть это как недостаток, потому что краткость вашего кода может выиграть от того, что UC является hittestable. Однако здесь не краткость - это скорость. На самом деле это важная функция, потому что она действительно уменьшает количество предметов, на которых тревеллер должен выполнять фактические пересечения!

Так что - либо реализуйте HitTestCore и возвращайте что-то ненулевое, либо вместо hittest для детей UserControl, а затем, имея правильный результат, но равный его дочернему элементу, используйте VisualTreeHelper.GetParent, пока вы не перейдете к UserControl, разыскивается.

Смежные вопросы