代码之家  ›  专栏  ›  技术社区  ›  Jon B

确定哪个控件最接近鼠标指针

  •  5
  • Jon B  · 技术社区  · 15 年前

    在我的C(.NET 2)应用程序中,我想确定哪个控件更靠近鼠标。

    我可以想出一些方法来解决这个问题。我可以使用 Control.Location 属性,但这只会给我顶部/左侧,鼠标可能在控件的另一侧。我可以计算一个控件的中心点,但是大的控件会扭曲这个中心点(靠近控件边缘会被视为靠近控件)。

    所以基本上我在画布上有一堆长方形和一个点。我需要找到离这一点最近的矩形。

    (理想情况下,我也希望知道点和矩形之间的距离)。

    有什么想法吗?

    5 回复  |  直到 11 年前
        1
  •  3
  •   James King    15 年前

    您需要找到以下内容:
    -到最近角的距离
    -到最近边缘的距离
    -(可选)到中心的距离

    基本上,您需要这三个值中较小的一个。取两个控件的最小值来确定哪个更接近。

    在加载表单时,首先迭代表单上的所有控件,然后创建下面的类集合。

    要查找最接近某个点的控件,请迭代集合(参见底部的代码)。以你目前为止发现的最小距离追踪控制。如果需要,可以测试containsPoint()。如果你找到一个控制点在控制范围内,你就得到了控制点(只要你没有重叠的控制点)。否则,当您到达集合的末尾时,找到距离中心/边缘最短的控件就是您的控件。

    public class HitControl {
    
        public Control ThisControl;
    
        private Rectangle ControlBounds;
        private Point Center;
    
        public HitControl (Control FormControl) {
            ControlBounds = FormControl.Bounds;
            Center = new Point(ControlBounds.X + (ControlBounds.Width/2), ControlBounds.Y + (ControlBounds.Height/2));
        }
    
        //  Calculate the minimum distance from the left, right, and center
        public double DistanceFrom(Point TestPoint) {
    
            //  Note:  You don't need to consider control center points unless
            //  you plan to allow for controls placed over other controls... 
            //  Then you need to test the distance to the centers, as well, 
            //  and pick the shortest distance of to-edge, to-side, to-corner
    
            bool withinWidth = TestPoint.X > ControlBounds.X && TestPoint.X < ControlBounds.X + ControlBounds.Width;
            bool withinHeight = TestPoint.Y > ControlBounds.Y && TestPoint.Y < ControlBounds.Y + ControlBounds.Height;
    
            int EdgeLeftXDistance = Math.Abs(ControlBounds.X - TestPoint.X);
            int EdgeRightXDistance = Math.Abs(ControlBounds.X + ControlBounds.Width - TestPoint.X);
    
            int EdgeTopYDistance = Math.Abs(ControlBounds.Y - TestPoint.Y);
            int EdgeBottomYDistance = Math.Abs(ControlBounds.Y + ControlBounds.Height - TestPoint.Y);
    
            int EdgeXDistance = Math.Min(EdgeLeftXDistance, EdgeRightXDistance);
            int EdgeYDistance = Math.Min(EdgeTopYDistance, EdgeBottomYDistance);
    
    
            // Some points to consider for rectangle (100, 100, 100, 100):
            //  - (140, 90):  Distance to top edge
            //  - (105, 10):  Distance to top edge
            //  - (50, 50):   Distance to upper left corner
            //  - (250, 50):  Distance to upper right corner
            //  - (10, 105):  Distance to left edge
            //  - (140, 105):  Distance to top edge
            //  - (105, 140):  Distance to left edge
            //  - (290, 105):  Distance to right edge
            //  - (205, 150):  Distance to right edge
            //  ... and so forth
    
    
            //  You're within the control
            if (withinWidth && withinHeight) {
                return Math.Min(EdgeXDistance, EdgeYDistance);
            }
    
            //  You're above or below the control
            if (withinWidth) {
                return EdgeYDistance;
            }
    
            //  You're to the left or right of the control
            if (withinHeight) {
                return EdgeXDistance;
            }
    
            //  You're in one of the four outside corners around the control.
            //  Find the distance to the closest corner
            return Math.Sqrt(EdgeXDistance ^ 2 + EdgeYDistance ^ 2);
    
    
        }
    
        public bool ContainsPoint (Point TestPoint) {
            return ControlBounds.Contains(TestPoint);
        }
    
    
    }
    
    
    
    //  Initialize and use this collection
    List<HitControl> hitControls = (from Control control in Controls
                                    select new HitControl(control)).ToList();
    
    Point testPoint = new Point(175, 619);
    double distance;
    double shortestDistance = 0;
    HitControl closestControl = null;
    
    foreach (HitControl hitControl in hitControls) {
    
        //  Optional... works so long as you don't have overlapping controls
        //  If you do, comment this block out
        if (hitControl.ContainsPoint(testPoint)) {
            closestControl = hitControl;
            break;
        }
    
        distance = hitControl.DistanceFrom(testPoint);
        if (shortestDistance == 0 || distance < shortestDistance) {
            shortestDistance = distance;
            closestControl = hitControl;
        }
    }
    
    if (closestControl != null) {
        Control foundControl = closestControl.ThisControl;
    }
    
        2
  •  1
  •   Community Mohan Dere    8 年前

    首先检查点是否在任何 rectangle . 如果没有,可以使用中的算法查找点与每个线段之间的距离。 this . 您还可以找到控件的4个段,因此您有一个由四个段(确定控件边)组成的列表(第一次启动),现在您可以找到最近的段,它的最近矩形。

        3
  •  0
  •   Onkelborg    15 年前

    你必须用矩形来思考:)

    1. 测试:鼠标在控制范围内吗?
    2. 如果没有:离任何一个边缘有多远?

    然后您必须知道您对哪个控件感兴趣,例如,表单是一个控件。

        4
  •  0
  •   Daniel MoÅ¡mondor    15 年前

    对于初学者,创建将计算从矩形边缘到某个任意点的距离的方法。此方法的签名应为:

    double DistanceFrom(Rect r, Point p);
    

    然后,对于最简单的尝试,迭代所有控件,计算距离,记住提供它的最小距离和控件。

    对于矩形距离,请检查 this 出来。

    编辑:

    事实上,您可以维护一个已排序的控件列表,这样您就可以始终拥有第一个更靠近顶部的控件,并在鼠标移动时维护该列表-这可能证明在速度方面更有效。有趣的问题是:)

        5
  •  0
  •   QYY    15 年前

    我同意丹尼尔的观点,我们需要: 两倍距离(矩形R,点P);

    但在此之前,我们需要: 两倍距离(R线,P点); 和 两点之间的双角(点p1,点p2);