在开发复杂的图形项目时,一个实用的起点是创建一个能够绘制矩形、圆形和其他形状的工具,同时还能移动、调整大小甚至旋转一些形状。本文将介绍一个基于C#的图形绘制工具,它不仅支持基本的绘图功能,还提供了丰富的属性设置和图形导出功能。
要完全理解这个代码,用户需要了解一些C#的基本概念,如反射、接口、继承等。此外,还有一个使用WPF技术的绘图工具版本,可以在找到。
该项目首先创建了一个包含所有可用绘图工具的工具箱。用户可以选择这些工具在主屏幕上进行绘制。项目还提供了一个属性包,允许用户动态更改形状的边框颜色、填充颜色、箭头宽度、文本框内容、文本大小等。
用户完成绘图后,可以选择将绘图导出为XML文件或JPG文件。下面是一个XML文件的示例,展示了如何保存图形数据:
<?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">
<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>
</ShapeList>
使用上述XML文件,项目可以在下次打开时加载这些文件,用户可以继续编辑他们的绘图。
定义了几个基本形状,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,创建了它们的等效结构并在所有地方使用它们。
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; }
}
// 更多代码...
}
LeShape类是一个抽象类,因为不希望用户在任何时候实例化它。相反,创建了ZoneShape、Rectangle等形状类,然后实例化它们,这样更有意义。
为了让用户能够移动和调整形状的大小,创建了另一个类BoundaryShape,它继承自LeShape,包含所有属性,而这个BoundaryShape只处理用户的鼠标移动,并且不会被序列化为XML文件。基本上所有形状都将从BoundaryShape继承,而BoundaryShape继承自LeShape。
public class RoundRectShape : BoundaryShape
{
private int radius = 10;
// 可以有圆角形状,默认半径为10像素。
public override void Paint(object sender, Graphics g)
{
Point[] pt = new Point[8];
// 绘制圆角矩形形状的代码...
}
}
这个Paint方法在BoundaryShape中有副本,不想使用它,所以用override作为修饰符。
ZoneShape有一个文本字段,当ZoneShape移动时,文本字段也会随之移动。这是通过在用户完成移动ZoneShape后,在BoundaryShape中引发事件,ZoneShape接受这个事件然后处理这个事件,移动文本字段参数来实现的。