如何画线并在面板中选择它 [英] How to draw line and select it in Panel
问题描述
我的程序可以使用 canvas.Drawline() 绘制线条.如何单击线条并更改此颜色(选择线条)?
My program can draw lines using canvas.Drawline(). How to click line and change this color (select line)?
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 &冷最后.
Coordinate line stored in coordFirs & coodLast.
推荐答案
这是一个合适的 Line
类:
Here is a suitable Line
class:
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;
}
}
为行定义一个列表,可能在类级别:
Define a List for the lines, probably at class level:
List<Line> lines = new List<Line>();
这里是如何用几行初始化它:
Here is how you can initialize it with a few lines:
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))));
这是点击十字路口的结果:
Here is the result of clicking on a crossing:
每当您添加、更改或删除一行时,您都需要通过触发 Paint
事件使 Panel
反映新闻:
Whenever you add, change or remove a line you need to make the Panel
reflect the news by triggering the Paint
event:
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
事件中进行测试:
In the MouseClick
event you do the test:
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
子类:>
To avoid flicker don't use the basic Panel
class as it isn't doublebuffered
. Instead use either a PictureBox
or a Label
(with AutoSize=false
) or a doublebuffered Panel
subclass:
class DrawPanel : Panel
{ public DrawPanel () { DoubleBuffered = true; } }
注意事项:
WinForms 中没有线条"这样的东西,只有各种颜色的像素.因此,要选择一条线,您需要存储它的两个端点的坐标,然后在单击时确定您是否点击了它.
There is no such thing as a 'Line' in WinForms, only pixels of various colors. So to select a line you need to store it's two endpoints' coordinates and then find out if you have hit it when clicking.
上面的例子展示了如何在数学中做到这一点.
The above example shows how to do it in math.
相反,可以通过将每条线绘制到位图并测试鼠标单击的像素来测试它.但是绘制这些位图还必须在幕后进行数学运算,并为位图分配空间,因此数学运算会更有效率..
Instead one could test each line by drawing it onto a bitmap and test the pixel the mouse has clicked. But drawing those bitmaps would have to do math behind the scenes as well and also allocate space for the bitmaps, so the math will be more efficient..
是的,Line
类对于一行这样简单的东西来说看起来有点长,但看看现在所有的事件代码是多么的短!那是因为责任是它们的归属!
Yes the Line
class looks a little long for such a simple thing a s a line but look how short all the event codes now are! That's because the responsiblities are where they belong!
还要注意在 WinForms 中进行任何绘图的第一条规则是:从不缓存或存储 Grahics
对象.事实上,您一开始就不应该使用 CreateGraphics
,因为Graphics
对象永远不会停留在范围内,它生成的图形也会不坚持(即在最小化-最大化序列中幸存下来)..
Also note the the first rule of doing any drawing in WinForms is: Never cache or store a Grahics
object. In fact you shouldn't ever use CreateGraphics
in the first place, as the Graphics
object will never stay in scope and the graphics it produces will not persist (i.e. survive a Minimize-maximize sequence)..
还要注意我如何传递Paint
事件参数的e.Graphics
对象到Line
实例,以便他们可以使用当前的 Graphics
对象绘制自己!
Also note how I pass out the e.Graphics
object of the Paint
event's parameters to the Line
instances so they can draw themselves with a current Graphics
object!
要选择更细的线,稍微修改距离检查可能会有所帮助..
To select thinner lines it may help to modify the distance check a little..
数学直接取自维基百科.
这篇关于如何画线并在面板中选择它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!