首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WPF: OnRender和Hit测试

WPF: OnRender和Hit测试
EN

Stack Overflow用户
提问于 2010-05-21 22:59:26
回答 3查看 1.6K关注 0票数 0

使用OnRender在屏幕上画图时,有没有办法对绘制的图形进行命中测试?

示例代码

代码语言:javascript
复制
    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绘制一些你不能对绘制的东西执行任何命中测试的东西?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-05-22 04:26:05

你理解的没错,没有办法对你使用DrawingContext方法绘制的图形进行命中测试,因为它们没有在可视化树中表示为对象。

票数 0
EN

Stack Overflow用户

发布于 2018-03-04 02:54:14

除非我完全误解了OP的问题,否则答案是肯定的,命中测试是基于在OnRender期间绘制的内容。您可以通过在覆盖OnRender的同一个类中覆盖OnMouseEnter来进行判断。在OnMouseEnter的覆盖中,尝试执行以下操作:

代码语言:javascript
复制
var htr = VisualTreeHelper.HitTest(this, e.GetPosition(this));
if (htr != null)
     System.Diagnostics.Debug.WriteLine("It's a hit!");

现在,我注意到您仍然在调用base.OnRender,这表明您正在尝试在Visual本来会呈现的内容之上进行绘制。那么,您不能做的是区分父类呈现的内容与您在重写中呈现的内容。但是,如果需要,只需将图形包含在它自己的Visual中即可。

票数 0
EN

Stack Overflow用户

发布于 2015-11-08 04:58:17

我的快速解决方案。这在许多方面都不理想,但它作为一个开始对我来说是可行的。如果有必要,可以很容易地重构以提高性能。

代码语言:javascript
复制
    /// <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();
        }
    }

在控件中声明实例

代码语言:javascript
复制
public RadarHitTestUtility HitTester = new RadarHitTestUtility();

您需要在某个时刻调用Clear(),以便命中测试集合中只存在当前对象。

代码语言:javascript
复制
HitTester.Clear();

在OnRender()中,当您希望记录绘制调用以进行命中测试时,可以使用AddEllipse()进行记录。

代码语言:javascript
复制
HitTester.AddEllipse(radarObject, radarObject.Point, actorRadius, actorRadius);

以下是我调用命中检查的方法

代码语言:javascript
复制
    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中并调用它,就可以创建一种不同类型的几何图形进行测试。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2883172

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档