|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionDrawTools 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
DocToolkit LibraryDocToolkit 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 timeEvery 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 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. 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
public virtual int HitTest(Point point)
{
return -1;
}
Derived classes use protected override bool PointInObject(Point point)
{
return rectangle.Contains(point);
// rectangle is class member of type Rectangle
}
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);
}
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 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 - RedoUndo-Redo functionality is added to the program using the article The Command Pattern and MVC Architectures by David Veeneman. Class
|
||||||||||||||||||||||