Click here to Skip to main content
6,595,854 members and growing! (18,256 online)
Email Password   helpLost your password?
Multimedia » General Graphics » Graphics     Intermediate License: The Code Project Open License (CPOL)

DrawTools

By Alex Fr

Sample Windows Forms application for drawing graphic objects in a window client area using drawing tools and mouse.
C#, Windows, .NET 1.1, .NET 2.0, GDI+, WinForms, VS.NET2003, VS2005, Dev
Posted:7 Oct 2004
Updated:24 Jan 2007
Views:213,812
Bookmarked:242 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
100 votes for this article.
Popularity: 9.42 Rating: 4.71 out of 5
4 votes, 4.1%
1

2

3
10 votes, 10.3%
4
83 votes, 85.6%
5

DrawTools

Introduction

DrawTools sample shows how to create a Windows Forms application for drawing graphic objects in a Windows client area using mouse and drawing tools. Drawing tools implemented in this sample are: Rectangle, Ellipse, Line, and Pencil. There are well-known techniques for creating such type of applications, like: interaction with mouse, flicker-free drawing, implementing of drawing and selection tools, objects selection, managing of objects Z-order etc. MFC developers may learn all this stuff from MFC sample DRAWCLI. DrawTools C# program reproduces some of DRAWCLI functionality and uses some design decisions from this sample.

DrawTools solution contains two projects: DrawTools Windows Forms application and DocToolkit Class Library. DrawTools implements specific application stuff, and DocToolkit contains some standard classes for file managing.

Main features of the DrawTools solution are described below.

DrawTools classes

Classes

  • DrawArea - user control which fills main application window client area. Contains instance of the GraphicsList class. Draws graphic objects, handles mouse input passing commands to GraphicsList.
  • GraphicsList - list of graphic objects. Contains ArrayList of graphic objects. Talks with each graphic object by generic way using DrawObject methods.
  • DrawObject - abstract base class for all graphic objects.
  • DrawRectangle - rectangle graphic object.
  • DrawEllipise - ellipse graphic object.
  • DrawLine - line graphic object.
  • DrawPolygon - polygon graphic object.
  • Tool - abstract base class for all drawing tools.
  • ToolPointer - pointer tool (neutral tool). Contains implementation for selection, moving, resizing of graphic objects.
  • ToolObject - abstract base class for all tools which create new graphic object.
  • ToolRectangle - rectangle tool.
  • ToolEllipse - ellipse tool.
  • ToolLine - line tool.
  • ToolPolygon - polygon tool.

DocToolkit Library

DocToolkit Library contains a set of classes which may be used for creation of document-centric Windows Forms applications. Instances of classes exported from the DocToolkit Library are kept in the main form of the DrawTools project and is used for general file-related operations.

Handling of Windows controls state at application idle time

Every Windows Forms application has a number of controls like menu items, buttons, toolbar buttons etc. Depending on current situation and user commands, these controls may have different states: enabled/disabled, checked/unchecked, visible/invisible etc. Every user action may change this state. Setting of controls' state in every message handler may be error-prone. Instead of this, it is better to manage controls' state in some function which is called after every user action. MFC has the great ON_UPDATE_COMMAND_UI feature which allows to update toolbar buttons' state at application idle time. Such a feature may be implemented also in .NET programs.

Consider the situation when user clicks the Rectangle toolbar button. This button should be checked, and previously active tool should be unchecked. Rectangle button message handler doesn't change form controls' state, it just keeps current selection in some variable. Idle message handler selects active tool and unselects inactive tool.

private void Form1_Load(object sender, System.EventArgs e)
{
    // Submit to Idle event to set controls state at idle time

    Application.Idle += delegate(object o, EventArgs a)
    {
        SetStateOfControls();
    };
}

public void SetStateOfControls()
{
    // Select active tool

    tbPointer.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Pointer);
    tbRectangle.Pushed = (drawArea.ActiveTool==DrawArea.DrawToolType.Rectangle);
    tbEllipse.Pushed  = (drawArea.ActiveTool == DrawArea.DrawToolType.Ellipse);
    tbLine.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Line);
    tbPolygon.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Polygon);

    menuDrawPointer.Checked = 
                      (drawArea.ActiveTool == DrawArea.DrawToolType.Pointer);
    menuDrawRectangle.Checked = 
                      (drawArea.ActiveTool == DrawArea.DrawToolType.Rectangle);
    menuDrawEllipse.Checked = 
                      (drawArea.ActiveTool == DrawArea.DrawToolType.Ellipse);
    menuDrawLine.Checked = (drawArea.ActiveTool == DrawArea.DrawToolType.Line);
    menuDrawPolygon.Checked = 
                      (drawArea.ActiveTool == DrawArea.DrawToolType.Polygon);

    // ...

}

// Rectangle tool is selected

private void CommandRectangle()
{
     drawArea.ActiveTool = DrawArea.DrawToolType.Rectangle;
}

Hit Test

DrawObject class has virtual HitTest function which detects whether a point belongs to graphic object:

public virtual int HitTest(Point point)
{
    return -1;
}

Derived classes use virtual PointInObject to make hit test. This function is called from HitTest. DrawRectangle class implements this function by a simple way:

protected override bool PointInObject(Point point)
{
    return rectangle.Contains(point);
    // rectangle is class member of type Rectangle

}

DrawLine implementation of this function is more complicated:

protected override bool PointInObject(Point point)
{
    GraphicsPath areaPath;
    Pen areaPen;
    Region areaRegion;

    // Create path which contains wide line

    // for easy mouse selection

    AreaPath = new GraphicsPath();
    AreaPen = new Pen(Color.Black, 7);
    AreaPath.AddLine(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
        // startPoint and EndPoint are class members of type Point

    AreaPath.Widen(AreaPen);

    // Create region from the path

    AreaRegion = new Region(AreaPath);

    return AreaRegion.IsVisible(point);
}

DrawPolygon function works by the same way, but AreaPath contains all lines in the polygon.

Serialization

GraphicList class implements ISerializable interface which allows to make binary serialization of the class object. DrawObject class has two virtual functions which are used for serialization:

public virtual void SaveToStream(SerializationInfo info, int orderNumber)
{
    // ...

}

public virtual void LoadFromStream(SerializationInfo info, int orderNumber)
{
  // ...

}

These functions are implemented in every derived class. Binary file has the following format:

Number of objects
Type name
Object
Type name
Object
...
Type name
Object

This allows to write generic serialization code in the GraphicList class without knowing any details about serialized objects:

private const string entryCount = "Count";
private const string entryType = "Type";


// Save list to stream

[SecurityPermissionAttribute(SecurityAction.Demand, 
                         SerializationFormatter=true)]
public virtual void GetObjectData(SerializationInfo info, 
                                     StreamingContext context)
{
    // number of objects

    info.AddValue(entryCount, graphicsList.Count);

    int i = 0;

    foreach ( DrawObject o in graphicsList )
    {
        // object type

        info.AddValue(
            String.Format(CultureInfo.InvariantCulture,
                "{0}{1}",
                entryType, i),
            o.GetType().FullName);

        // object itself

        o.SaveToStream(info, i);

        i++;
    }
}

// Load from stream

protected GraphicsList(SerializationInfo info, StreamingContext context)
{
    graphicsList = new ArrayList();

    // number of objects

    int n = info.GetInt32(entryCount);
    string typeName;
    object drawObject;

    for ( int i = 0; i < n; i++ )
    {
        // object type

        typeName = info.GetString(
            String.Format(CultureInfo.InvariantCulture,
                "{0}{1}",
            entryType, i));

        // create object by type name using Reflection

        drawObject = Assembly.GetExecutingAssembly().CreateInstance(
            typeName);

        // fill object from stream

        ((DrawObject)drawObject).LoadFromStream(info, i);

        graphicsList.Add(drawObject);
    }

}

Undo - Redo

Undo-Redo functionality is added to the program using the article The Command Pattern and MVC Architectures by David Veeneman. Class Command is abstract base class for all commands representing user activity: CommandAdd, CommandChangeState, CommandDelete, CommandDeleteAll. Every Command-derived class can make two basic operations: Undo which returns draw area to the state before executing this command, and Redo which executes this command again. CommandChangeState is used for resizing, moving of objects and changing objects properties. Class UndoManager keeps list of executed commands (history) and makes operations Undo, Redo and Add command to History. Undo-Redo functionality is implemented only in 2005 version of DrawTools project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Alex Fr


Member

Occupation: Software Developer
Location: Israel Israel

Other popular General Graphics articles:

  • A flexible charting library for .NET
    Looking for a way to draw 2D line graphs with C#? Here's yet another charting class library with a high degree of configurability, that is also easy to use.
  • CxImage
    CxImage is a C++ class to load, save, display, transform BMP, JPEG, GIF, PNG, TIFF, MNG, ICO, PCX, TGA, WMF, WBMP, JBG, J2K images.
  • 3D Pie Chart
    A class library for drawing 3D pie charts.
  • Barcode Image Generation Library
    This library was designed to give an easy class for developers to use when they need to generate barcode images from a string of data.
  • ImageStone
    An article on a library for image manipulation.
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 162 (Total in Forum: 162) (Refresh)FirstPrevNext
Generalrotation Pinmemberdoktorekx11:34 19 Sep '09  
GeneralRe: rotation PinmemberAlex Fr20:29 19 Sep '09  
GeneralRe: rotation Pinmemberdoktorekx12:33 5 Nov '09  
GeneralTop Left ruler around the canvas PinmemberNatrajan Pillai22:31 19 Aug '09  
GeneralAnnotation Pinmemberoldginger4:25 5 Aug '09  
GeneralHow do you make the drawing canvas transparent? Pinmembernb1forxp27:37 18 Jul '09  
GeneralRe: How do you make the drawing canvas transparent? PinmemberAlex Fr8:33 18 Jul '09  
Questionhow did you create the control Pinmembernaimishranderi4:57 4 Jul '09  
AnswerRe: how did you create the control PinmemberAlex Fr7:21 4 Jul '09  
GeneralDrawing objects co-ordinates Pinmemberdivyesh143223:47 12 May '09  
GeneralRe: Drawing objects co-ordinates PinmemberAlex Fr5:06 15 May '09  
Generalwhat is your dtl format? PinmemberSouthmountain9:01 9 May '09  
GeneralRe: what is your dtl format? PinmemberAlex Fr5:03 15 May '09  
GeneralProblem, Drawing polygon over scanned image(TIFF) Pinmemberraj2313620:22 13 Apr '09  
QuestionCannot access a disposed object. in DocToolkit try X closing the MainForm Pinmembervitsyn13:27 9 Feb '09  
Generalwonderful job! PinmemberSouthmountain7:36 26 Dec '08  
GeneralFill color inside shapes Pinmemberesabarinath0:45 21 Aug '08  
QuestionRe: Fill color inside shapes PinmemberTL Wallace6:32 5 Dec '08  
GeneralFunction Undo works incorrectly after loading from file [modified] Pinmembertsptomsk2:57 14 Aug '08  
GeneralFix PinmemberAlex Fr5:11 15 Aug '08  
GeneralRe: Fix Pinmembertsptomsk16:32 16 Aug '08  
GeneralAutomatic Scrollbars - Dynamic Solution PinmemberBill Hanson12:07 29 May '08  
GeneralRe: Automatic Scrollbars - Dynamic Solution PinmemberGIS_Developer17:29 10 Nov '08  
Generalwhat's mean "First, perform all steps from the static method."? PinmemberMember 52990916:34 18 Feb '09  
GeneralRe: what's mean "First, perform all steps from the static method."? Pinmemberjamesyla0:41 23 Sep '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 24 Jan 2007
Editor: Chris Maunder
Copyright 2004 by Alex Fr
Everything else Copyright © CodeProject, 1999-2009
Web16 | Advertise on the Code Project