2010-05-21 3 views
0

OnRender를 사용하여 화면에 무언가를 그릴 때 그려진 그래픽에서 히트 테스트를 수행 할 수있는 방법이 있습니까?WPF : OnRender 및 히트 테스트

샘플 코드

protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) 
    { 
     base.OnRender(drawingContext); 

     drawingContext.DrawRectangle(Brushes.Black, null, new Rect(50, 50, 100, 100)); 
    } 

분명히 하나가 히트 테스트를 수행하거나 이것에 대해 잘못 생각해야 할 것이다 그려진 사각형에 대한 참조? DrawingVisual을 사용할 수 있다는 것을 알고 있습니다. 이해가 정확하다면 OnRender를 사용하여 그려진 것들에 대한 히트 테스트를 수행 할 수 없다는 점이 궁금합니다.

답변

0

당신은 시각적 트리에서 객체로 표현되지 않았기 때문에 DrawingContext 메소드를 사용하여 그려진 그래픽에 히트 테스팅을 할 방법이 없다는 것을 이해했습니다.

+0

귀하의 확인과 설명에 감사드립니다. : –

+0

미안 OP의 질문을 오해하지 않는 한,이 것은 정확하지 않습니다. –

+0

OP는 그가 DrawingContext 또는 DrawingContext를 사용하여 그리는 원과 같은 다른 것을 사용하여 그리는 직사각형에 마우스가 닿는 것을 알고 싶어합니다. 그래서, 어떻게 할 수있는 방법을 찾았습니까? – bitbonk

-1

내 빠른 해결책. 여러면에서 이상적이지는 않지만 처음에는 저에게 효과적입니다. 필요한 경우 쉽게 성능을 위해 리팩토링 할 수 있습니다.

/// <summary> 
    /// Provides basic hit testing 
    /// </summary> 
    public class RadarHitTestUtility 
    { 
     public Dictionary<Tuple<int,int>, HitContainer> HitStorage = new Dictionary<Tuple<int, int>, HitContainer>(); 

     public void AddEllipse(RadarObject radarObject, Point point, double x, double y) 
     { 
      // Geometry used for hit testing 
      var elipse = new EllipseGeometry 
      { 
       Center = point, 
       RadiusX = x, 
       RadiusY = y, 
      }; 

      var key = new Tuple<int, int>((int)point.X, (int)point.Y); 

      var hit = new HitContainer 
      { 
       Geometry = elipse, 
       Bounds = elipse.Bounds, 
       RadarObject = radarObject 
      }; 

      if(!HitStorage.ContainsKey(key)) 
       HitStorage.Add(key, hit); 
     } 

     /// <summary> 
     /// Gets first radar object whose center point is within distance of point. 
     /// </summary> 
     public HitContainer GetSimpleHit(Point point, int distance = 5) 
     { 
      foreach (var hit in HitStorage) 
      { 
       if (Math.Abs(hit.Key.Item1 - point.X) <= distance && Math.Abs(hit.Key.Item2 - point.Y) <= distance) 
       { 
        hit.Value.Intersection = IntersectionDetail.NotCalculated; 
        return hit.Value; 
       } 
      } 
      return default(HitContainer); 
     } 

     /// <summary> 
     /// Gets first radar object that contains point 
     /// </summary> 
     public HitContainer GetHit(Point point) 
     { 
      var PreCheckDistance = 50; 

      foreach (var hit in HitStorage) 
      { 
       if (Math.Abs(hit.Key.Item1 - point.X) <= PreCheckDistance && Math.Abs(hit.Key.Item2 - point.Y) <= PreCheckDistance) 
       { 
        if (hit.Value.Geometry.FillContains(point)) 
        { 
         hit.Value.Intersection = IntersectionDetail.FullyInside; 
         return hit.Value; 
        }      
       } 
      } 
      return default(HitContainer); 
     } 

     /// <summary> 
     /// Gets first radar object that intersects with geometry 
     /// </summary> 
     public HitContainer GetHit(Geometry geometry) 
     { 
      var PreCheckDistance = 50; 
      var GeometryCheckTolerance = 1; 
      var center = geometry.Bounds.Center(); 

      foreach (var hit in HitStorage) 
      { 
       if (Math.Abs(hit.Key.Item1 - center.X) <= PreCheckDistance && Math.Abs(hit.Key.Item2 - center.Y) <= PreCheckDistance) 
       { 
        var result = hit.Value.Geometry.FillContainsWithDetail(geometry, GeometryCheckTolerance, ToleranceType.Absolute); 
        if (result != IntersectionDetail.Empty) 
        { 
         hit.Value.Intersection = result; 
         return hit.Value; 
        } 
       } 
      } 
      return default(HitContainer); 
     } 

     public class HitContainer 
     { 
      public RadarObject RadarObject { get; set; } 
      public Geometry Geometry { get; set; } 
      public Rect Bounds { get; set; } 
      public IntersectionDetail Intersection { get; set; } 
     } 

     public void Clear() 
     { 
      HitStorage.Clear(); 
     } 
    } 

은 제어

public RadarHitTestUtility HitTester = new RadarHitTestUtility(); 

당신은 현재의 객체가 히트 테스트 컬렉션의 존재 때문에 어떤 점에서 지우기()를 호출해야합니다에서 인스턴스를 선언합니다.

의 OnRender()는 무승부 호출이 히트 테스트 목적으로 기록 할 때 내가 히트 검사를 요구하고 어떻게

다음
HitTester.AddEllipse(radarObject, radarObject.Point, actorRadius, actorRadius); 

이야) (AddEllipse와 함께 녹음 할 수 있습니다에서

HitTester.Clear(); 

private void MouseDownHandler(object sender, MouseButtonEventArgs e) 
    { 
     IsMouseDown = true; 
     Cursor = Cursors.Hand; 
     DragInitialPosition = Mouse.GetPosition(this); 
     DragInitialPanOffset = CanvasData.PanOffset; 

     var hit = FindElementUnderClick(sender, e); 
     if (hit != null) 
     { 
      SelectedItem = hit.RadarObject.Actor; 
     }    
    } 

    private RadarHitTestUtility.HitContainer FindElementUnderClick(object sender, MouseEventArgs e) 
    { 
     var position = e.GetPosition((UIElement)sender);    
     return HitTester.GetHit(position); 
    } 

타원 이외의 모양을 테스트하려면 RadarHitTestUtility에 적절한 녹음 방법을 추가하고 호출하여 다른 종류의 기하학을 만들어 테스트합니다. ainst.

0

OP의 질문을 완전히 오해하지 않는 한 대답은 YES입니다. 히트 테스트는 OnRender 동안 그려지는 내용을 기반으로합니다. OnRender을 무시하는 동일한 클래스에서 OnMouseEnter을 재정 의하여 알 수 있습니다. OnMouseEnter의 재정이 시도 : 이제

var htr = VisualTreeHelper.HitTest(this, e.GetPosition(this)); 
if (htr != null) 
    System.Diagnostics.Debug.WriteLine("It's a hit!"); 

을, 당신은 여전히 ​​당신이 Visual 그렇지 않으면 렌더링 될지 위에 그릴하려고 제안하는 base.OnRender를 호출하고 확인할 수 있습니다. 그러면 할 수없는 일은 상위 클래스가 렌더링 한 것과 상위 클래스에서 렌더링 한 것을 구별하는 것입니다. 그러나 필요한 경우 드로잉 자체를 Visual에 보관하십시오.

+0

UIElement가 아니기 때문에 직사각형을 치는 습관을 알 수있는 방법이 없습니다. 히트 테스트는'OnRender'를 사용하여 그렸던 컨트롤을 반환 할 것이지만'DrawingContex'를 사용하여 그린 그래픽은 반환하지 않을 것입니다. – bitbonk

관련 문제