使用 GDI+ 绘制和编辑线条
这是一个实现基本图形用户界面,用于在 PictureBox 上绘制和编辑线条的代码。
引言
我的第一篇文章! 我希望这段代码能帮助到别人... 我在网上搜索,发现许多开发者(像我一样)都在寻找实现线条绘制/编辑的方法,由于 C# 在这方面的限制,这似乎是一件非常难实现的事情... GDI+ 似乎并没有提供很大的帮助,所以我决定自己做一个解决方案,它看起来运行得相当好 :D。 就是它了。
很久以前...
我开始考虑创建一个 Line 自定义控件,但由于透明度和 Z 顺序的问题,结果非常糟糕(你不能选择一个在另一个控件下面的控件)... 然后我意识到线条本身必须直接写入 PictureBox
,以避免这些问题,并且每次都必须重写 PictureBox.Image
...
当点击 DrawLine
按钮后,两个红色标记放置在 PictureBox
中,并绘制一条线。 这两个标记实际上是两个自定义控件,继承自 UserControl
,但它们只是一个红色正方形(可以个性化,一个十字,一个圆等),内部没有代码。 在标记控件创建时,我附加了三个鼠标事件(MouseDown
,MouseMove
,MouseUp
),以便允许用户在 PictureBox
周围拖动它们。 每次移动一个标记时,所有线条(以及背景图像)都会被重绘。 非常简单,不是吗?
让我们看看一些代码...
private struct Line
{
public MarkControl mark1;
public MarkControl mark2;
public int Width;
}
此对象表示在 PictureBox
上绘制的线条。 它包含关于两个标记(它们的位置,属性 Center
)和线条宽度的信息(可以通过鼠标右键编辑)。 绘制的每条线都会添加到 Line
的数组中;每次重绘图像时,都会使用此信息重绘所有线条。
//Redraws all the lines and the background too
private void Redraw()
{
if(bmpBack!=null)
image.Image = (Bitmap)bmpBack.Clone();
else
{
image.Image = new Bitmap(image.Width,image.Height);
Graphics.FromImage(image.Image).Clear(Color.White);
}
foreach(Line l in Lines)
{
DrawLine(l);
}
image.Refresh();
}
这是 DrawLine
函数
//Simply draws a line
private void DrawLine(Line line)
{
Graphics g = null;
g = Graphics.FromImage(image.Image);
g.DrawLine(new Pen(Color.Black,(float)line.Width),
line.mark1.Center.X,line.mark1.Center.Y,
line.mark2.Center.X,line.mark2.Center.Y);
g.Dispose();
}
太棒了,它工作了! 但是有一个问题... 当在一个大的表面上绘制线条时,例如桌面背景,程序运行速度非常慢,因为图像太重,无法一直重新加载... 那么? 我编写了一个新的 Redraw()
,它以相同的方式重绘所有线条,但仅刷新图像中已修改的区域,通过 PictureBox
的 Invalidate(Region region)
方法... 这个技巧代码需要稍微增强一下,但现在运行得很好! 如果有人有更好的想法,请告诉我! 这是代码
//Redraws all the lines and a part of the background
private void Redraw(Line line, Point p)
{
Graphics.FromImage(image.Image).DrawImage(bmpBack,0,0,
image.Image.Width, image.Image.Height);
foreach(Line l in Lines)
{
DrawLine(l);
}
Region r = getRegionByLine(line,p);
image.Invalidate(r);
image.Update();
}
//Returns the region to update
private Region getRegionByLine(Line l, Point p)
{
GraphicsPath gp = new GraphicsPath();
gp.AddPolygon(new Point[]{l.mark1.Center,l.mark2.Center,p,l.mark1.Center});
RectangleF rf = gp.GetBounds();
gp.Dispose();
rf.Inflate(100f,100f);
return new Region(rf);
}
Line l
是已被移动的线条,p
是标记已移动到的 Point
... 我通过从线条和点获取三角形来计算区域,然后获取包含它的矩形,然后将其膨胀以获得更好的结果...
只有线条?
这个项目也可以修改为其他几何图形,如圆形、椭圆、矩形、多边形等等! 添加更多标记并将它们用作几何点!
结束
是的,就这样了 ;) 我希望这篇文章能帮助到别人,就像其他 Code Project 文章每天都在我的工作中帮助我一样。 请给我发送反馈或/和建议! butch.er@tin.it。
再见!