一个优秀的C#开源绘图软件 DrawTools

1、Extensions to DrawTools

Author

I develop software for a leading healthcare system in Northern Illinois.

Draw Tools Redux Image

Introduction

Alex Fr provided an excellent set of drawing tools in his DrawTools article and these tools serve as a basis for this article, which expands on the original toolset in the following ways:

  1. In addition to the basic Rectangle, Ellipse, Line and Scribble tools, this version adds PolyLine, Filled Ellipse, Filled Rectangle, Text and Image tools
  2. Multiple drawing Layers
  3. Zooming
  4. Panning
  5. Rotation

In this article, I will describe how Layers were implemented, as well as the Text and Image tools.

Background

See the original DrawTools article for details on how the basic application is built, class structure, etc.

It is also assumed that the reader has a working understanding of GDI+ fundamentals, including Matrices. For an excellent introduction to GDI+, see www.bobpowell.net.

Implementing Layers

Adding Layers to the application involved adding two classes, Layer and Layers, where Layer defines a single Layer and Layers defines the collection of Layers in an ArrayList.

Each Layer exposes the following properties:

private string _name;
private bool _isDirty;
private bool _visible; private bool _active; private GraphicsList _graphicsList; 

Note that the Layer contains the GraphicsList - this is the key to the whole thing - each Layer contains its own list of drawing objects instead of DrawArea. DrawArea is modified to declare a Layers collection instead of a GraphicsList collection:

// Define the Layers collection
private Layers _layers;

When DrawArea is initialized, the Layers are initialized by creating the first Layer and setting it Active and Visible:

public DrawArea()
{
// create list of Layers, with one default active visible layer
_layers = new Layers();
_layers.CreateNewLayer("Default"); _panning = false; _panX = 0; _panY = 0; // This call is required by the Windows.Forms Form Designer. InitializeComponent(); } 

In the Layers class, the CreateNewLayer() method actually creates the new Layer:

/// <summary>
/// Create a new layer at the head of the layers list and set it /// to Active and Visible. /// </summary> /// <param name="theName">The name to assign to the new layer</param> public void CreateNewLayer(string theName) { // Deactivate the currently active Layer if(layerList.Count > 0) ((Layer)layerList[ActiveLayerIndex]).IsActive = false; // Create new Layer, set it visible and active Layer l = new Layer(); l.IsVisible = true; l.IsActive = true; l.LayerName = theName; // Initialize empty GraphicsList for future objects l.Graphics = new GraphicsList(); // Add to Layers collection this.Add(l); } 

Note that any one or all Layers can be visible at the same time, but only one Layer may be active at any time.

You can control the Layers in the sample application by clicking on the Current Layer: name at the bottom of the application window - Click on the name ("Default") to open the Layers dialog:

From this dialog, you can Add new Layers, change the names of the Layer(s), and change the Layer(s) visibility and which Layer is Active. The "New Layer" column is checked whenever you click the "Add Layer" button. To delete Layer(s), simply check the "Deleted" column and close the dialog with the "Close" button. Remember only one Layer may be active at any one time. You will be reminded of this if you attempt to have more than one Layer active. Also note the Active Layer must be Visible.

When the application runs, each object that is drawn is added to the GraphicsList maintained by the active Layer. Note this relationship is preserved through saving and re-opening a drawing file.

Layers come in very handy when you want to draw "on top of" another image. For example, the image at the top of this article contains two layers. The following image shows the same picture with the Background Layer turned off:

Here is the same drawing with the Drawing Layer invisible and the Background Layer visible:

Objects on Layers which are visible but not active cannot be selected, moved, deleted, etc.

Each drawing object is added to the correct Layer by the AddNewObject() method in the ToolObject class:

protected void AddNewObject(DrawArea drawArea, DrawObject o)
{
     int al = drawArea.TheLayers.ActiveLayerIndex;
     drawArea.TheLayers[al].Graphics.UnselectAll();
     o.Selected = true;
   o.Dirty = true;
   drawArea.TheLayers[al].Graphics.Add(o);
     drawArea.Capture = true; drawArea.Refresh(); } 

Implementing Zooming, Panning, and Rotation

Zooming, Panning, and Rotation are implemented by adding a few variables and some code to the MainForm and DrawArea classes.

Zooming is controlled by buttons on the form, and also by the mouse wheel when Ctrl is held down.

Pan is controlled by the Hand button on the form, and can be cancelled by a right-click.

Rotation is controlled by buttons on the form - note Rotation affects the entire drawing.

Here is an example of all three in use:

Screenshot - DrawToolsRedux-3.png

The heart of this code is the BackTrackMouse() method, which takes the "apparent" mouse position and converts it to a valid point based on the current Zoom level, Pan position, and Rotation:

/// <summary>
 /// Back Track the Mouse to return accurate coordinates regardless of /// zoom or pan effects. /// Courtesy of BobPowell.net <seealso cref="http://www.bobpowell.net/backtrack.htm"/> /// </summary> /// <param name="p">Point to backtrack</param> /// <returns>Backtracked point</returns> public Point BackTrackMouse(Point p) { // Backtrack the mouse... Point[] pts = new Point[] { p }; Matrix mx = new Matrix(); mx.Translate(-this.ClientSize.Width / 2, -this.ClientSize.Height / 2, MatrixOrder.Append); mx.Rotate(_rotation, MatrixOrder.Append); mx.Translate(this.ClientSize.Width / 2 + _panX, this.ClientSize.Height / 2 + _panY, MatrixOrder.Append); mx.Scale(_zoom, _zoom, MatrixOrder.Append); mx.Invert(); mx.TransformPoints(pts); return pts[0]; }

This routine comes from Bob Powell's excellent website. Through the use of the GDI+ Matrix class, the mouse point passed to this method is moved (Translate), Rotated, and Scaled based on the current PanX, PanY, Zoom, and Rotation values. The important thing to remember is that anytime you need to determine where the mouse pointer actually is in your drawing, you must call this method. You will see this method used throughout the program in the DrawArea class as well as others. An example of its usage is shown here:

private void DrawArea_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
     lastPoint = BackTrackMouse(e.Location);
     if (e.Button == MouseButtons.Left)
         tools[(int)activeTool].OnMouseDown(this, e); else if (e.Button == MouseButtons.Right) { if (_panning == true) _panning = false; ActiveTool = DrawArea.DrawToolType.Pointer; } //OnContextMenu(e); }

The current zoom level is controlled by the following simple routine:

private void AdjustZoom(float _amount)
{
    drawArea.Zoom += _amount;
    if (drawArea.Zoom < .1f)
        drawArea.Zoom = .1f;
    if (drawArea.Zoom > 10) drawArea.Zoom = 10f; drawArea.Invalidate(); SetStateOfControls(); }

Then in the DrawArea.Paint() method, the zoom, pan, and rotation values are used to alter the way the canvas is painted:

private void DrawArea_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    Matrix mx = new Matrix();
    mx.Translate(-this.ClientSize.Width / 2, -this.ClientSize.Height / 2, MatrixOrder.Append); mx.Rotate(_rotation, MatrixOrder.Append); mx.Translate(this.ClientSize.Width / 2 + _panX, this.ClientSize.Height / 2 + _panY, MatrixOrder.Append); mx.Scale(_zoom, _zoom, MatrixOrder.Append); e.Graphics.Transform = mx; SolidBrush brush = new SolidBrush(Color.FromArgb(255, 255, 255)); e.Graphics.FillRectangle(brush, this.ClientRectangle); // Draw objects on each layer, in succession so we get the correct layering. // Only draw layers that are visible if (_layers != null) { int lc = _layers.Count; for (int i = 0; i < lc; i++) { if(_layers[i].IsVisible == true) if(_layers[i].Graphics != null) _layers[i].Graphics.Draw(e.Graphics); } } DrawNetSelection(e.Graphics); brush.Dispose(); }

Update - 8/25/2007 - Individual Object Rotation & Bug Fixes

The primary advancement in this update is the ability to rotate individual objects - when one or more objects are selected, clicking the Rotate tools will rotate those objects instead of the entire drawing surface.

There is one caveat, however - the selection rectangle for the rotated object is not rotated - if someone can help with this, I would greatly appreciate it!

This update also includes several small bug fixes reported by users - thanks to all for reporting!

History

  • 3/6/2007
    • Original article uploaded to The Code Project
  • 3/6/2007
    • Updated to include more information on zoom/pan/rotation
  • 8/25/2007
    • Updated Individual Object Rotation
  • 9/27/2007
    • Added the missing links to the new source code
  • 12/23/2009
    • Added Tooltip control which appears when mouse is over an object. Tooltip displays the Center coordinates of the object for Rectangle, Ellipse and Image objects. For other objects, Tooltip displays starting and ending coordinates. Text objects do not display Tooltip.
      This was implemented adding the Tooltip control to the ToolPointer class. Each Draw Object fills the TipText property and the MouseMove event in ToolPointer controls when the Tooltip is displayed and removed from the canvas. This implementation is not perfect, as the Tooltip flashes when displayed so is meant as an example of one way information about the object can be displayed.
      Perhaps a better way to do this would be to display information about the object in a separate "Information Window" and then only when the object is selected.
    • See the new source code for details.
  • 6/23/2010
    • Updated project to include object ordering fix that corrects the way objects are stacked when a file is opened
    • Updated project to Visual Studio 2010
    • See the new source code for details
  • 10/4/2011
    • Corrected several issues with Layers and layering

2、DrawTools 2014

Author

Arnault Bonafos, 16 Jan 2014

Introduction  

Current alternative is based on the CodeProject DrawTools 2005, built under Visual Studio C# 2010.

The main goal of this tip is to post and save the DrawTools project back to the community after some enhancements. The tip is short, as the improvements are minor.

A few improvements have been made, as follows:

  1. Add scroll bars to the draw area
  2. Handling of AutoScrollPosition in the different tools provided by the original project
  3. Export of the drawing with jpeg format  

Background

Knowledge of C# is required, but this project shows the basics of UI programming under '.NET'.

Architecture

Dynamic handling for showing scroll bars

A dirty boolean is handled in GraphicsList, the canvas document, where it should be. To this Dirty property is associated a DocumentDirtyObserver interface.

public interface DocumentDirtyObserver
{
    void IsDirty(GraphicsList gList);
}

Every time the document is modified, look with the different draw tools, its Dirty property is set to true and any DocumentDirtyObserver is fired.

public bool Dirty
{
    get
    {
        return this.dirty;
    }
    set { this.dirty = value; if (this.dirty) { NotifyDirty(); } } } 

The class DrawArea implements the DocumentDirtyObserver, and every time the document is 'dirty', it calls AdjustRendering, which in turn computes the bounding box of the canvas document (GraphicsList).

void AdjustRendering()
{
    Size docSize;

    if (this.GraphicsList != null)
    {
        docSize = this.GraphicsList.GetSize();
        docSize.Width += 20; docSize.Height += 20; AutoScrollMinSize = docSize; } else { AutoScrollMinSize = new Size(0, 0); } Invalidate(); } 

This way, it implements the Mode/View/Controller design pattern, the GraphicsList being the model, DrawArea being the View and the draw Tools being the controller.

In the future, Dirty property should be handled in only one place (it is also handled in the DrawArea class).

Export the graph to JPEG format

The DrawTools 2014 architecture is good enough to be followed to implement a new menu strip option.

    1. Add a menu item to the menu bar

      Simply open the MainForm designer and add the option

    2. Link the MenuItem to a MainForm method to trigger the CommandExportToJpg

      private void CommandExportToJpg()
      {
          docManager.ExportToJpg();
      }
      
    3. Implement the user interface logic in the DocManager ExportToJpg

      I let you look at the DocManager.ExportToJpg

    4. Subscribe MainForm ExportEvent method implementation to DocManager event.
docManager.ExportEvent += docManager_ExportEvent;
  1. Implement MainForm logic to actually transform image bitmap into JPEG file.

    All is in the source code, I don't copy it here as it is essentially technical stuff.

Result

The result is best shown by the following graph, obtained with a customized version of the DrawTools.

Points of Interest

Architecture is already well thought in the original project, please read the original document for insight about it.

There was a bug where the status bar was hiding the horizontal scroll bar, but after some thorough inspection of the code, it has been fixed.

History

  1. Added scroll bars to the draw area
  2. Added a status bar
  3. Fixed a bug on the context menu when the scroll bar position is not 0
  4. Export of the drawing with jpeg format
  5. Fixed an OutOfMemoryException with JPEG format export

Special thank you to Alex, who originally posted the DrawTools project on CodeProject.

 

License

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

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: drawtools2005是一个常用的绘图工具软件。它提供了丰富的功能,方便用户进行各种类型的绘图工作。 首先,drawtools2005具有简洁直观的用户界面,使用户可以快速上手。它提供了多种绘图选项,例如画笔、橡皮擦、形状工具以及文本工具等,让用户能够根据需要进行自由创作。 其次,drawtools2005支持多种文件格式的导入和导出,如常见的图片格式(JPEG、PNG等)以及矢量图格式(SVG、PDF等)。这意味着用户可以将自己绘制的图形保存为不同的文件类型,方便在其他软件或平台上进行使用和编辑。 此外,drawtools2005还提供了一些高级功能,如图层管理和历史记录。图层管理功能使用户可以对绘图元素进行分组和排序,方便进行图形的调整和修改。而历史记录功能则可以帮助用户追溯和撤销之前的操作,避免由于错误操作导致的不必要的麻烦。 最后,作为一款老牌的绘图工具软件drawtools2005拥有稳定的性能和广泛的用户群体。用户可以通过在线社区获取更多的绘图技巧和资源,与其他绘图爱好者进行互动和交流。 综上所述,drawtools2005是一款功能齐全、易于使用的绘图工具软件。它能够满足用户的绘图需求,并提供了丰富的功能和支持,使用户能够创作出精美的图形作品。 ### 回答2: drawtools2005是一款功能强大的绘图工具软件,它提供了许多方便实用的绘图功能。首先,它具有丰富的画笔工具,包括铅笔、钢笔、毛笔等,可以满足不同绘图需求。其次,drawtools2005还有大量的颜色选择,可以自由调整颜色,使绘图更加鲜明生动。此外,该软件还配备了多种效果工具,如模糊、锐化、马赛克等,可以独特地处理图像效果。 在使用drawtools2005时,用户可以方便地进行图层编辑,可以自由创建、复制、删除图层,有利于更好地组织绘图作品。此外,软件还支持多种图像格式的导入和导出,如JPEG、PNG、BMP等,方便用户的文件互通。另外,drawtools2005还支持文本编辑功能,可以在绘图中添加文字,使作品更加有趣。 除了基本的绘图功能外,drawtools2005还提供了一些高级功能,如滤镜效果、图形变换等,可以让用户对图像进行进一步处理和修饰。而且,软件界面简洁明了,操作简单易学,即使是初学者也可以轻松上手。 总结来说,drawtools2005是一款功能强大、操作简便的绘图软件。不论是想要绘制简单的线条还是创造精美的图像,都可以通过该软件轻松实现。无论是专业绘图师还是普通用户,drawtools2005都能满足绘图需求,是一款值得推荐的绘图工具软件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值