65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (15投票s)

2011年6月20日

GPL3

3分钟阅读

viewsIcon

87194

downloadIcon

9840

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

引言

这是我业余时间开发的一个绘图工具,它可以绘制矩形、圆形和其他形状,您还可以移动、调整大小,甚至旋转某些形状。这将是一个复杂图形项目的有用起点。

背景

为了完全理解这段代码,用户必须理解一些 C# 概念,例如反射、接口、继承等。还有另一个使用 WPF 的绘图工具版本,您可以在 GraphicsDrawingToolWPF.aspx中找到它。

Using the Code

该项目首先创建一个工具箱,其中包含所有可用的绘图工具。 如下面的屏幕截图所示

Untitled-2.jpg

然后用户可以选择这些工具在主屏幕上绘图,如下面的屏幕截图所示

Untitled-3.jpg

该项目还为用户提供了一个属性包,用于动态更改形状边框颜色、填充颜色、箭头宽度、文本框内容、文本大小等。

Untitled-4.jpg

该项目最终为用户提供了将绘图导出为 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创建 ZoneShapeRectangle 形状类,然后我们实例化它们,这更有意义。

为了让用户移动、调整形状大小,我创建了另一个类 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 接口。

我将 LeShapeIShape 实现设为虚拟方法。 然后在其继承的类中,有选择地重写这些虚拟方法。

ZoneShape 有一个文本字段,想法是当 ZoneShape 移动时,文本字段也移动。 这是在用户完成移动 zoneShape 后实现的,然后在 BoundaryShape中引发一个事件,ZoneShape 接受此事件然后处理此事件,移动文本字段参数。

用户的鼠标移动由 LeCanvas 类处理,LeCanvas 类然后将此事件传递给其所有屏幕上的形状。 然后每个形状决定其操作。 无论是开始绘图,还是移动形状,还是调整形状大小。

结束语

我想为计算机世界做出贡献,在那里我一直喜欢从他人那里学习,或者为了那些可能认为我的工作有用的人。

© . All rights reserved.