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

时间:2010-10-19 14:22:07

标签: c# .net winforms graphics controls

在我的C#(.NET 2)应用程序中,我想确定哪个控件是鼠标的壁橱。

我可以想到一些方法可以做到这一点,但这些方法不会正常工作。我可以使用Control.Location属性,但这只是给我上/下,鼠标可能在控件的另一侧。我可以计算一个控件的中心点,但是大的控件会使这个偏斜(靠近控件的边缘计算为靠近控件)。

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

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

有什么想法吗?

5 个答案:

答案 0 :(得分:3)

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

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

通过迭代表单上的所有控件并创建下面类的集合来加载表单时开始。

要查找与点最近的控件,请迭代集合(请参阅底部的代码)。使用到目前为止找到的最小距离来跟踪控件。你可以测试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;
}

答案 1 :(得分:1)

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

答案 2 :(得分:0)

你必须考虑矩形:)

  1. 测试:鼠标是否在控制范围内?
  2. 如果没有:距离任何一条边有多远?
  3. 然后你必须知道你感兴趣的控件,例如,窗体是一个控件..

答案 3 :(得分:0)

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

double DistanceFrom(Rect r, Point p);

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

对于矩形距离,请检查this

编辑:

事实上,你可以维护一个排序的控件列表,这样你就可以随时拥有一个更靠近顶部的控件,并在鼠标移动时保持该列表 - 在速度方面可以证明它更有效。有趣的问题虽然:)

答案 4 :(得分:0)

我同意丹尼尔的意见,我们需要: double DistanceFrom(Rect r,Point p);

但在此之前,我们需要: double DistanceFrom(Line r,Point p); 和 double AngleBetweenPoints(Point p1,Point p2);