使用 C# 和 Winforms 的图形绘制工具






4.65/5 (15投票s)
使用 C# 和 Winforms 的图形绘制工具
引言
这是我业余时间开发的一个绘图工具,它可以绘制矩形、圆形和其他形状,您还可以移动、调整大小,甚至旋转某些形状。这将是一个复杂图形项目的有用起点。
背景
为了完全理解这段代码,用户必须理解一些 C# 概念,例如反射、接口、继承等。还有另一个使用 WPF 的绘图工具版本,您可以在 GraphicsDrawingToolWPF.aspx中找到它。
Using the Code
该项目首先创建一个工具箱,其中包含所有可用的绘图工具。 如下面的屏幕截图所示
然后用户可以选择这些工具在主屏幕上绘图,如下面的屏幕截图所示
该项目还为用户提供了一个属性包,用于动态更改形状边框颜色、填充颜色、箭头宽度、文本框内容、文本大小等。
该项目最终为用户提供了将绘图导出为 XML 文件或 jpg 文件的选项。
<?xml version="1.0" encoding="utf-8"?>
<ShapeList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ShapeList>
<LeShape xsi:type="LeRectangle">
<ShowBorder>true</ShowBorder>
<LeBorderColor>
<A>255</A>
<R>0</R>
<G>0</G>
<B>0</B>
</LeBorderColor>
<BorderWidth>1</BorderWidth>
<Rect>
<X>300</X>
<Y>157</Y>
<Width>79</Width>
<Height>65</Height>
</Rect>
<LeFromColor>
<A>30</A>
<R>255</R>
<G>0</G>
<B>0</B>
</LeFromColor>
<LeToColor>
<A>30</A>
<R>255</R>
<G>255</G>
<B>255</B>
</LeToColor>
<LightAngle>225</LightAngle>
<Fill>true</Fill>
</LeShape>
<LeShape xsi:type="RoundRectShape">
<ShowBorder>true</ShowBorder>
<LeBorderColor>
<A>255</A>
<R>0</R>
<G>0</G>
<B>0</B>
</LeBorderColor>
<BorderWidth>1</BorderWidth>
<Rect>
<X>174</X>
<Y>230</Y>
<Width>84</Width>
<Height>74</Height>
</Rect>
<LeFromColor>
<A>255</A>
<R>0</R>
<G>0</G>
<B>0</B>
</LeFromColor>
<LeToColor>
<A>255</A>
<R>127</R>
<G>255</G>
<B>212</B>
</LeToColor>
<LightAngle>225</LightAngle>
<Fill>true</Fill>
<Radius>10</Radius>
</LeShape>
<LeShape xsi:type="ZoneShape">
<ShowBorder>true</ShowBorder>
<LeBorderColor>
<A>255</A>
<R>0</R>
<G>0</G>
<B>0</B>
</LeBorderColor>
<BorderWidth>1</BorderWidth>
<Rect>
<X>132</X>
<Y>97</Y>
<Width>90</Width>
<Height>84</Height>
</Rect>
<LeFromColor>
<A>30</A>
<R>255</R>
<G>0</G>
<B>0</B>
</LeFromColor>
<LeToColor>
<A>30</A>
<R>255</R>
<G>255</G>
<B>255</B>
</LeToColor>
<LightAngle>225</LightAngle>
<Fill>true</Fill>
<TextField>
<ShowBorder>true</ShowBorder>
<LeBorderColor>
<A>255</A>
<R>0</R>
<G>0</G>
<B>0</B>
</LeBorderColor>
<BorderWidth>1</BorderWidth>
<Rect>
<X>237</X>
<Y>112</Y>
<Width>58</Width>
<Height>22</Height>
</Rect>
<LeFromColor>
<A>30</A>
<R>255</R>
<G>0</G>
<B>0</B>
</LeFromColor>
<LeToColor>
<A>30</A>
<R>255</R>
<G>255</G>
<B>255</B>
</LeToColor>
<LightAngle>225</LightAngle>
<Fill>true</Fill>
<Caption>Shape 2</Caption>
<LeTextFont>
<Size>10</Size>
<Name>Tahoma</Name>
<Style>Regular</Style>
</LeTextFont>
<LeTextColor>
<A>255</A>
<R>255</R>
<G>0</G>
<B>0</B>
</LeTextColor>
<TextSize>10</TextSize>
</TextField>
<Caption>Shape 2</Caption>
</LeShape>
</ShapeList>
</ShapeList>
使用上述 XML 文件,项目可以在下次打开此文件,用户可以继续编辑他们的绘图。
完成后,用户可以选择将其导出为 jpg 文件。
以下是该项目的说明。
我定义了几个基本形状,LeShape
是基本类,为了序列化我创建的形状,我重新定义了LeColor
结构,如下所示
public struct LeColor
{
public int A;
public int R;
public int G;
public int B;
public LeColor(Color color)
{
this.A = color.A;
this.R = color.R;
this.G = color.G;
this.B = color.B;
}
public static LeColor FromColor(Color color)
{
return new LeColor(color);
}
public Color ToColor()
{
return Color.FromArgb(A, R, G, B);
}
}
由于我们无法将 C# Font
和 Color
类序列化为 XML,我创建了它们对应的结构并在所有地方使用它们。
此绘图工具的基本类是 LeShape
public abstract class LeShape : IShape
{
private bool showBorder = true;
public bool ShowBorder
{
get { return showBorder; }
set
{
showBorder = value;
LeCanvas.self.Canvas.Invalidate();
}
}
private LeColor borderColor = new LeColor(Color.Black);
public LeColor LeBorderColor
{
get { return borderColor; }
set
{
borderColor = value;
LeCanvas.self.Canvas.Invalidate();
}
}
[XmlIgnore]
public Color BorderColor
{
get { return LeBorderColor.ToColor(); }
set { LeBorderColor = new LeColor(value); }
}
private int borderWidth = 1;
public int BorderWidth
{
get { return borderWidth; }
set
{
borderWidth = value;
LeCanvas.self.Canvas.Invalidate();
}
}
private Rectangle bounds;
[XmlIgnore]
public Rectangle Boundary
{
set { bounds = value;
Rect =new LeRect(value);
}
get { return bounds; }
}
...
public LeShape()
{
path = new GraphicsPath();
objectsInPath = new ArrayList();
}
如您所见,这个 LeShape
类是 abstract
类,因为我们不希望用户在任何时候都实例化它。相反,我们基于此 LeShape
创建 ZoneShape
、Rectangle
形状类,然后我们实例化它们,这更有意义。
为了让用户移动、调整形状大小,我创建了另一个类 BoundaryShape
,它继承自 LeShape
,其中包含所有属性,而此 BoundaryShape
仅处理用户的鼠标移动,并且它不会被序列化到 XML 文件。
基本上所有形状都将继承自 BoundaryShape
,而 BoundaryShape
继承自 LeShape
。
public class RoundRectShape : BoundaryShape
{
private int radius = 10;
我们可以有一个圆角形状,默认半径为 10 像素。
我们使用以下 paint
方法绘制此圆角 Rectangle
形状。
public override void Paint(object sender, Graphics g)
{
Point[] pt = new Point[8];
path = new GraphicsPath();
path.AddLine(pt[4], pt[5]);
path.AddArc(new Rectangle(pt[6], new Size(radius, radius)),
90, 90);
path.AddLine(pt[6], pt[7]);
if (path != null)
{
g.FillPath(new System.Drawing.Drawing2D.LinearGradientBrush(
Boundary, FromColor, ToColor, LightAngle), path);
}
}
此 paint
方法在 BoundaryShape
中有一个副本,我们不想使用它,因此我们将其作为修饰符进行重写。
您还将看到 LeShape
实现了 IShape
接口。
我将 LeShape
的 IShape
实现设为虚拟方法。 然后在其继承的类中,有选择地重写这些虚拟方法。
ZoneShape
有一个文本字段,想法是当 ZoneShape
移动时,文本字段也移动。 这是在用户完成移动 zoneShape 后实现的,然后在 BoundaryShape
中引发一个事件,ZoneShape
接受此事件然后处理此事件,移动文本字段参数。
用户的鼠标移动由 LeCanvas
类处理,LeCanvas
类然后将此事件传递给其所有屏幕上的形状。 然后每个形状决定其操作。 无论是开始绘图,还是移动形状,还是调整形状大小。
结束语
我想为计算机世界做出贡献,在那里我一直喜欢从他人那里学习,或者为了那些可能认为我的工作有用的人。