Click here to Skip to main content
15,880,543 members
Articles / Multimedia / GDI+

A Graphics Drawing Tool by using C# and Winforms

Rate me:
Please Sign up or sign in to vote.
4.65/5 (17 votes)
23 Jun 2011GPL33 min read 84.1K   9.7K   49   12
A Graphics Drawing Tool by using C# and Winforms

Introduction

This is a drawing tool I developed in my spare time, it can draw rectangle, circle, and other shapes, you can also move, resize, even rotate some of the shapes. It will be a useful starting point of a complicated graphics project.

Background

To fully understand this code, the user has to understand some of the C# concepts, reflection, interfaces, inheritance, etc. There is another version of this drawing tool by using WPF, you can find it on GraphicsDrawingToolWPF.aspx.

Using the Code

This project first creates a tool box which contains all the available drawing tools. Like the following screen capture:

Untitled-2.jpg

Then the user can select these tools to draw on main screen, a screen capture like the following:

Untitled-3.jpg

This project also comes up with a propertybag for user, to dynamically change shape border color, fill color, arrow width, text box content, text size, etc.

Untitled-4.jpg

This project finally provides the user with an option of exporting the drawing as XML file or jpg file.

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">
  <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>

With the above XML file, the project can open this file next time, users can keep editing their drawings.

Once finished, users can choose export it to jpg file.

Following is the explanation of the project.

I have defined several basic shapes, the LeShape is basic class, in order to serialize the shapes I have created, I have redefined LeColor structure, like the following:


C#
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);
        }
    }

As we can't serialize C# Font and Color class to XML, I created their equivalent structures and used them everywhere.

This drawing tool's basic class is LeShape:

C#
    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();
        }

As you can see, this LeShape class is abstract class, as we don't want user to instantiate it at any time. Instead we create ZoneShape, Rectangle shape classes based on this LeShape, then we instantiate them, it makes more sense.

In order to let user move, resize shapes, I made another class BoundaryShape, it inherits from LeShape, which contains all the properties, while this BoundaryShape only handles user's mouse movement, and it won't be serialized to XML file.

Basically all the shapes will be inherited from BoundaryShape, and BoundaryShape inherited from LeShape.

C#
public class RoundRectShape : BoundaryShape
{
        private int radius = 10; 

We can have corner radius shape, by default radius is 10 px.

We used the following paint method to draw this cornered Rectangle shape.

C#
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);
        }
    }

This paint method has a copy in BoundaryShape, we don't want to use it, therefore we put override as modifier.

You will also see the LeShape implemented IShape interface.

I made LeShape's IShape implementation virtual method. Then at its inherited classes, selectively rewrite these virtual methods.

ZoneShape has a text field, the idea is when ZoneShape moves, text field moves as well. This is achieved when user has finished move zoneshape, then raise an event at BoundaryShape, ZoneShape accepts this event then processes this event, moves the text field parameters.

User's mouse movement was handled by LeCanvas class, LeCanvas class then passes this event to all its on screen shapes. Each shape then decides its actions. Either it's a drawing start, or move a shape or resize a shape.

End Note

I wanted to contribute to the computer world, where I always enjoyed learning from others, or for others that maybe think my work is useful.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionOverlaps Pin
Member 941083225-Oct-12 9:22
Member 941083225-Oct-12 9:22 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey20-Feb-12 0:22
professionalManoj Kumar Choubey20-Feb-12 0:22 
QuestionHow can i use this Articles for more than one pictureBox in one form? Pin
vahid_m_20085-Dec-11 23:31
vahid_m_20085-Dec-11 23:31 
AnswerRe: How can i use this Articles for more than one pictureBox in one form? Pin
Lewis Liu L6-Dec-11 22:53
Lewis Liu L6-Dec-11 22:53 
GeneralRe: How can i use this Articles for more than one pictureBox in one form? Pin
vahid_m_20088-Dec-11 1:12
vahid_m_20088-Dec-11 1:12 
Questionhow can I rotate a shape Pin
ferfr14-Nov-11 18:37
ferfr14-Nov-11 18:37 
QuestionHow can i use this Articles without DrowTool form? Pin
vahid_m_200817-Oct-11 4:48
vahid_m_200817-Oct-11 4:48 
AnswerRe: How can i use this Articles without DrowTool form? Pin
Lewis Liu L17-Oct-11 9:28
Lewis Liu L17-Oct-11 9:28 
GeneralMy vote of 3 Pin
SubsonicDesignOfficial25-Jun-11 1:13
SubsonicDesignOfficial25-Jun-11 1:13 
Questioncould you please put some screenshot? Pin
Southmountain23-Jun-11 5:47
Southmountain23-Jun-11 5:47 
AnswerRe: could you please put some screenshot? Pin
Lewis Liu L23-Jun-11 11:31
Lewis Liu L23-Jun-11 11:31 
NewsReally helpful Pin
pcm_it20-Jun-11 1:24
pcm_it20-Jun-11 1:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.