【问题标题】:How to draw line and select it in Panel如何画线并在面板中选择它
【发布时间】:2015-10-03 07:11:01
【问题描述】:

我的程序可以使用 canvas.Drawline() 绘制线条。如何单击线条并更改此颜色(选择线条)?

private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;

        private void Form1_Load(object sender, EventArgs e)
        {
            canvas=panel1.CreateGraphics();
        }

坐标线存储在 coordFirs & coodLast 中。

【问题讨论】:

  • 你的目标是什么:Winforms? WPF? ASP? ...?? 总是相应地标记您的问题! - 如果是 Winforms,这里有两个提示: 1) NEVER 尝试缓存 Graphics 对象! 从不!! - 所有绘图都必须使用Paint 事件参数的e.Graphics 对象完成! - 2)winforms中没有“线”之类的东西,只有各种颜色的像素。因此,要选择一条线,您需要存储它的两个端点的坐标,然后在单击时确定是否点击了它。这可以通过数学或通过查看像素来完成。为了获得更好的建议,我们需要有关您的目标的更多信息!
  • 对于必要的数学,请参阅here! - 而不是使用两个愚蠢的列表,您应该定义一个 Line 类,其中包含一条线应该具有的所有信息,例如颜色、宽度、选择状态以及使用 Graphics 对象绘制自身的方法和查看是否有 Point 的方法!

标签: c# winforms canvas graphics


【解决方案1】:

这里有一个合适的Line类:

class Line
{
    public Color LineColor { get; set; }
    public float Linewidth { get; set; }
    public bool Selected { get; set; }
    public Point Start { get; set; }
    public Point End { get; set; }

    public Line(Color c, float w, Point s, Point e)
    { LineColor = c; Linewidth = w; Start = s; End = e;    }

    public void Draw(Graphics G)
    { using (Pen pen = new Pen(LineColor, Linewidth)) G.DrawLine(pen, Start, End); }

    public bool HitTest(Point Pt)
    {
        // test if we fall outside of the bounding box:
        if ((Pt.X < Start.X && Pt.X < End.X) || (Pt.X > Start.X && Pt.X > End.X) ||
            (Pt.Y < Start.Y && Pt.Y < End.Y) || (Pt.Y > Start.Y && Pt.Y > End.Y)) 
            return false;
        // now we calculate the distance:
        float dy = End.Y - Start.Y;
        float dx = End.X - Start.X;
        float Z = dy * Pt.X - dx * Pt.Y + Start.Y * End.X - Start.X * End.Y;
        float N = dy * dy + dx * dx;
        float dist = (float)( Math.Abs(Z) / Math.Sqrt(N));
        // done:
        return dist < Linewidth / 2f;
    }

}

为行定义一个列表,可能在类级别:

    List<Line> lines = new List<Line>();

你可以用几行代码来初始化它:

for (int i = 0; i < 20; i++) lines.Add(new Line(Color.Black, 4f, 
    new Point(R.Next(panel1.Width), R.Next(panel1.Height)), 
    new Point(R.Next(panel1.Width), R.Next(panel1.Height))));

这是点击十字路口的结果:

每当您添加、更改或删除一行时,您需要通过触发 Paint 事件使 Panel 反映新闻:

panel1.Invalidate();

这里是Panel的Paint事件:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    foreach (Line L in lines) L.Draw(e.Graphics);
}

在MouseClick 事件中您进行测试:

private void panel1_MouseClick(object sender, MouseEventArgs e)
{
    foreach(Line L in lines) 
            L.LineColor = L.HitTest(e.Location) ?  Color.Red : Color.Black;
    panel1.Invalidate();
}

为避免闪烁,请不要使用基本的Panel 类,因为它不是doublebuffered。而是使用PictureBox 或Label(带有AutoSize=false)或doublebuffered Panel 子类:

class DrawPanel : Panel 
{     public DrawPanel ()   { DoubleBuffered = true; }   }

注意事项:

  • 在 WinForms 中没有“线”之类的东西,只有各种颜色的像素。因此,要选择一条线,您需要存储它的两个端点的坐标,然后在单击时确定是否点击了它。

  • 上面的例子展示了如何在数学中做到这一点。

  • 相反,可以通过将每一行绘制到位图上来测试每一行,并测试鼠标单击的像素。但是绘制这些位图也必须在幕后进行数学运算,并为位图分配空间,因此数学运算会更有效率..

  • 是的,Line 类对于这么简单的一行来说看起来有点长,但看看现在所有的事件代码有多短!那是因为责任在他们所属的地方!

  • 还要注意在 WinForms 中进行任何绘图的第一条规则是:从不缓存或存储 Grahics 对象。事实上,您一开始就不应该使用 CreateGraphics,因为Graphics 对象永远不会停留在范围内,它产生的图形也不会持久化(即在最小化-最大化序列中存活)..

  • 还要注意我如何传递将Paint 事件的参数的e.Graphics 对象传递给Line 实例,以便它们可以使用当前的Graphics 对象绘制自己!

  • 要选择较细的线条,稍微修改一下距离检查可能会有所帮助..

  • 数学直接取自Wikipedia。

【讨论】:

  • 是否可以更改此代码以删除所选行?
  • 当然。它在List&lt;T&gt; 中,所以一旦你找到它,你可以做一个lines.Remove(foundLine)。
【解决方案2】:

您可以在点击时更改所有内容的颜色。通过使用特定对象的点击事件。

我给你一个按钮的例子。如果您单击按钮,则面板的颜色将发生变化。您可以根据需要修改代码。

private List<Point> coordFirst = new List<Point>();
    private List<Point> coordLast = new List<Point>();
    public Graphics canvas;



    private void Form1_Load(object sender, EventArgs e)
    {
        canvas = panel1.CreateGraphics();
    }





    private void panel1_Click(object sender, EventArgs e)
    {
        panel1.BackColor = Color.Blue;
    }

    private void nonSelectableButton3_Click(object sender, EventArgs e)
    {
        panel1.BackColor = Color.BurlyWood;
    }

【讨论】:

  • 不是面板背景色,而是改变线条的颜色(如果选择了线条)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多