Introduction

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

Background

To fulled undertand this code, 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 http://www.codeproject.com/KB/cs/GraphicsDrawingToolWPF.aspx
   

Using the code 

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 following:


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, therefore I created their equivalent structures and used them everywhere. 

This drawing tool's basic class is 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();
        }
		 

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

In order to let user move, resize shapes, I made another class BoundaryShape, it inherit from LeShape, which contains all the properties, while this BoundaryShape only handle 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.

 

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

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

We used following paint method draw this cornered Rectangle shape. 

    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 a event at BoundaryShape, ZoneShape accept this event then process this event, move the text field parameters.

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

.

Points of Interest  

Did you learn anything interesting/fun/annoying while writing the code? Did you do anything particularly clever or wild or zany?

History  

In oder to contribute to the computer world, which I always enjoyed to learn from others, or for others maybe think of my work is useful.   

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架