【问题标题】:How do I detect click on a line in Windows Forms如何检测点击 Windows 窗体中的一行
【发布时间】:2016-03-14 06:28:00
【问题描述】:

我有一个 winforms 应用程序

这是我的代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication12
{
    public partial class Form1 : Form
    {
        Graphics gr;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            gr = this.CreateGraphics();

            MyLine myline = new MyLine();
            myline.P1 = new Point(100, 0);
            myline.P2 = new Point(200, 80);

            gr.DrawLine(new Pen(Color.Red), myline.P1,myline.P2);


            Rectangle r = new Rectangle(0, 0, 50, 50);


            gr.DrawRectangle(new Pen(Color.Teal, 5), r);

            if (r.Contains(0,25)) MessageBox.Show("within");

        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            gr.Clear(this.BackColor);
        }


    }
}

class MyLine
{    
    public Point P1 {get; set;}
    public Point P2 { get; set; }
}

我的问题是这个..

我可以画一个矩形,我可以看看里面是否有一个点。

所以我可以将程序扩展为当点击表单在矩形内时说“是”。 Rectangle 有一个 Contains 功能,非常棒。

但我想对 Line 做同样的事情。

问题在于,winforms 没有 Line 类。我可以编写自己的 Line 类,但问题仍然存在.. 如何找到点击是否落在它上面?

我注意到WPF有这样一个类How do I recognize a mouse click on a line?

但我使用的是winforms。

【问题讨论】:

  • @MarkHall 你说的是一个高度为 1 的矩形......虽然这不适用于对角线,除非有办法转动矩形
  • 我知道也许我不需要全局并且可以在两个函数中分别使用 Graphics gr
  • 注意-reza 的回答,对于矩形你可以做bool b = gp.IsVisible(point) || gp.IsOutlineVisible(point, pen); 例如stackoverflow.com/questions/4816297/…
  • 也相关 - 当单击表单、面板或按钮时,扫描所有对象的列表以查看单击了哪个形状的想法。并为每个形状拥有自己的对象,并且该对象将具有 isHit 方法,如这里的答案stackoverflow.com/questions/1279091/…

标签: c# .net winforms graphics gdi+


【解决方案1】:

使用GraphicsPath.IsOutlineVisible方法可以判断用指定Pen绘制时指定点是否在路径轮廓线下。您可以设置笔的宽度。

因此您可以创建一个GraphicsPath,然后使用GraphicsPath.AddLine 在路径中添加一行,并检查路径是否包含该点。

示例:

下面的方法,使用指定的宽度检查p是否在端点p1p2的线上。

您可以使用更宽的宽度来增加容差,或者如果线条宽于 1:

//using System.Drawing;
//using System.Drawing.Drawing2D;
bool IsOnLine(Point p1, Point p2, Point p, int width = 1)
{
    using (var path = new GraphicsPath())
    {
        using (var pen = new Pen(Brushes.Black, width))
        {
            path.AddLine(p1, p2);
            return path.IsOutlineVisible(p, pen);
        }
    }
}

【讨论】:

  • 我有一个带有标签 (label1) 的表单。我似乎没有得到真正的返回/写入标签的文本。 pastebin.com/raw.php?i=mKt8qr8k 即使我给出了 10 作为宽度的参数。另外,width=1 是否直接等同于 pen 的参数大小?例如如果 pen 是 new Pen(Color.Black,10) 那么这是否对应于宽度参数 10?
  • 问题出在你的代码上。您应该使用MouseDown 事件并将e.Location 作为p 传递。该方法经过测试并且工作正常。使用width= 1 检查鼠标是否准确在线,但很难点击宽度=1 的线,因此您可以使用 3 作为宽度。
  • 由于您可以将任何形状添加到路径中,您可以使用GraphicsPath.IsOutlineVisible 检查该点是否位于任何形状的边界上。您也可以使用GraphicsPath.IsVisible 来检查形状是否包含该点。
  • 原来我使用您的代码但没有 MouseDown 功能的方式(那个 pastebin 链接)很好,除了一个错误,它来自您自己的代码。它应该是 path.AddLine(p1,p2) 但你做了 (p1,p1)。您可能想要纠正这一点。尽管您甚至没有进行测试就几乎是正确的,但给我留下了深刻的印象!顺便说一句,我点击 width=1 没有问题我也很高兴看到宽度参数完全对应于线条的粗细。
  • 感谢您的评论并指出那个错误,错误是在这里重命名参数后出现的:),我更正了这个。
【解决方案2】:

我实现了一个简单的Line 类来检查是否有一个点落在线上。
您可以从Form_Click 事件中捕获鼠标位置

这是sn-p

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication
{
    public partial class Form1 : Form
    {
        Line myLine;
        int x1 = 10;
        int x2 = 40;
        int y1 = 0;
        int y2 = 30;
        public Form1()
        {
            InitializeComponent();
            myLine = new Line() { Start = new Point(x1, y1), Stop = new Point(x2, y2), Epsilon = 10 };
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0));
            e.Graphics.DrawLine(pen, x1, y1, x2, y2);
            pen.Dispose();
        }

        private void Form1_Click(object sender, EventArgs e)
        {
            MouseEventArgs me = (MouseEventArgs)e;
            bool contain = myLine.contain(new Point(me.X,me.Y));
        }
    }

    public class Line
    {
        public Point Start { get; set; }
        public Point Stop { get; set; }
        public float Epsilon { get; set; }

        public bool contain(Point p)
        {
            // y = mx + c
            float m = (Stop.Y - Start.Y) / (Stop.X - Start.X);
            float c = Stop.Y - (m * Stop.X);
            return p.X >= Math.Min(Start.X, Stop.X)
                && p.X <= Math.Max(Start.X, Stop.X)
                && p.Y >= Math.Min(Start.Y, Stop.Y)
                && p.Y <= Math.Max(Start.Y, Stop.Y)
                && Math.Abs(Math.Abs(p.Y) - Math.Abs((m * p.X) + c)) < epsilon; //with relax rules
                //&& (p.Y == (m*p.X)+c); // strict version
        }
    }

更新
注意 X1 == X2 的情况。它会抛出异常。

【讨论】:

  • 谢谢。我认为这会涉及到一些差异化......看起来你做了一些 dy/dx。我注意到它不适用于较粗的线条,例如15 的论点。Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0),15);
  • 我喜欢 form1_click 中的 MouseEventArgs me = (MouseEventArgs)e; 然后 me.X me.Y 作为 mouseup 功能的有趣替代品
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 2016-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多