原作者:Alex Farber

CodeProject - DrawTools(画图工具)_控件

介绍

 

DrawTools示例告诉你怎么创建一个Windows窗体,来使用鼠标和画图工具在窗体上的可用区域画图。这个示例中实现了以下画图工具:矩形,椭圆形,直线和铅笔。其中有一些众所周知的技术来创建这个程序,比如说:鼠标的交互,无闪烁画图,实现了画图和工具选择,物体选择,控制物体的Z轴次序,等等。MFC开发者可以从DRAWCLI.这个MFC示例中了解到所有这些特性。DrawTools这个C#程序复制了一些DRAWCLI的功能,并且在这个例子中使用了一些设计决定。

 

 

 

DrawTools解决方案包括两个工程:DrawTools窗体应用程序和DocToolkit类库。DrawTools实现了一些特别的应用程序功能,DocToolkit包含有一些用于文件管理的标准类库。

以下描述了DrawTools 解决方案的主要特点:

 


CodeProject - DrawTools(画图工具)_ide_02

 

  • DrawArea用来填充主程序在窗体上可用区域的用户控件。包括GraphicsList 类的实例。绘制图形对象, 处理鼠标输入,把(鼠标)命令传给GraphicsList

     

  • GraphicsList图形对象的列表。包括图形对象的ArrayList。通过使用DrawObject类中的方法与其他图形对象通讯

     

  • DrawObject所有图形对象的抽象基类。

     

  • DrawRectangle矩形图形对象。

     

  • DrawEllipise椭圆形图形对象。

     

  • DrawLine直线图形对象。

     

  • DrawPolygon多边形图形对象。

     

  • Tool所有画图工具的抽象基类。

     

  • ToolPointer箭头工具 (中性工具)。包含选择,移动,改变图形对象大小的实现。

     

  • ToolObject所有工具的抽象基类,用来创建新的图形对象。ToolRectangle矩形工具。

     

  • ToolEllipse椭圆形工具。

     

  • ToolLine直线工具。

     

  • ToolPolygon多边形工具。

     

 

 

DocToolkit 类库

DocToolkit类库包含一些类的集合,这些类用来创建文档中心(document-centric)的窗体应用程序。从DocToolkit类库输出的类的实例被保存在DrawTools工程的主窗口中,用作一般的文件操作。

 

  • DocManager: 处理文件操作:打开,新建,保存,更新窗体标题,为Windows Shell注册文件类型。创建这个类引用了Chris Sells 的文章 Creating Document-Centric Applications in Windows Forms

     

  • DragDropManager: 在Windows Form应用程序中允许你通过拖拽的方式从浏览器(文件浏览器)中打开文件。

     

  • MruManager: 管理大多数最近使用的文件列表。

     

  • PersistWindowState: 允许你把最近一次的窗口状态保存到注册表 ,在窗体载入的时候重新获得。来源: Saving and Restoring the Location, Size and Windows State of a .NET Form By Joel Matthias.

     

在程序空闲的时候操作Windows的控件状态

 

每一个Windows窗体应用程序都会有许多控件,比如说菜单项,按钮,工具栏按钮等等。根据当前状态和用户的命令,这些控件可能有不同的状态:enabled/disabledchecked/unchecked, visible/invisible 等等。用户的每一个操作可能改变这些状态。在每一个消息句柄中改变控件的状态可能导致错误。取而代之的方法是,在用户每一个操作后调用一些函数,在这些函数中管理控件的状态。MFC有一个很好的特性叫ON_UPDATE_COMMAND_UI它允许在应用程序的空闲时间更新工具栏按钮的状态。这个特性也可以在.NET程序中实现。

 

 

考虑一下这种情形:当用户点击工具栏上的Rectangle按钮,这个按钮就要显示为被选中,前一个激活的工具就要显示为没选中。Rectangle按钮的消息句柄并没有改变窗体控件的状态,它只是在一些变量中保存了当前的选择。空闲的消息句柄选择激活的工具,取消未激活工具的选择。


 

 

CodeProject - DrawTools(画图工具)_应用程序_03private void Form1_Load(object sender, System.EventArgs e)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    // Submit to Idle event to set controls state at idle time
CodeProject - DrawTools(画图工具)_句柄_07    Application.Idle += new EventHandler(Application_Idle);
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03private void Application_Idle(object sender, EventArgs e)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    SetStateOfControls();
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03public void SetStateOfControls()
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    // Select active tool
CodeProject - DrawTools(画图工具)_句柄_07    tbPointer.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Pointer);
CodeProject - DrawTools(画图工具)_句柄_07    tbRectangle.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Rectangle);
CodeProject - DrawTools(画图工具)_句柄_07    tbEllipse.Pushed  = (drawArea.ActiveTool == DrawArea.DrawToolType.Ellipse);
CodeProject - DrawTools(画图工具)_句柄_07    tbLine.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Line);
CodeProject - DrawTools(画图工具)_句柄_07    tbPolygon.Pushed = (drawArea.ActiveTool == DrawArea.DrawToolType.Polygon);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    menuDrawPointer.Checked = 
CodeProject - DrawTools(画图工具)_句柄_07                      (drawArea.ActiveTool == DrawArea.DrawToolType.Pointer);
CodeProject - DrawTools(画图工具)_句柄_07    menuDrawRectangle.Checked = 
CodeProject - DrawTools(画图工具)_句柄_07                      (drawArea.ActiveTool == DrawArea.DrawToolType.Rectangle);
CodeProject - DrawTools(画图工具)_句柄_07    menuDrawEllipse.Checked = 
CodeProject - DrawTools(画图工具)_句柄_07                      (drawArea.ActiveTool == DrawArea.DrawToolType.Ellipse);
CodeProject - DrawTools(画图工具)_句柄_07    menuDrawLine.Checked = (drawArea.ActiveTool == DrawArea.DrawToolType.Line);
CodeProject - DrawTools(画图工具)_句柄_07    menuDrawPolygon.Checked = 
CodeProject - DrawTools(画图工具)_句柄_07                      (drawArea.ActiveTool == DrawArea.DrawToolType.Polygon);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    // CodeProject - DrawTools(画图工具)_应用程序_06
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03// Rectangle tool is selected
CodeProject - DrawTools(画图工具)_应用程序_03private void CommandRectangle()
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07     drawArea.ActiveTool = DrawArea.DrawToolType.Rectangle;
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03

Hit Test

DrawObject 类有一个叫HitTest的虚拟函数,用来侦测是否Point属于图形对象。

CodeProject - DrawTools(画图工具)_应用程序_03public virtual int HitTest(Point point)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    return -1;
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03

 

继承类使用虚拟的PointInObject来做点击测试。这个函数调用自HitTestDrawRectangle类使用了一种简单的方法实现了这个函数:

 

 

CodeProject - DrawTools(画图工具)_应用程序_03protected override bool PointInObject(Point point)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    return rectangle.Contains(point);
CodeProject - DrawTools(画图工具)_句柄_07    // rectangle is class member of type Rectangle
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03

DrawLine对这个函数的实现更加复杂:

 

CodeProject - DrawTools(画图工具)_应用程序_03protected override bool PointInObject(Point point)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    GraphicsPath areaPath;
CodeProject - DrawTools(画图工具)_句柄_07    Pen areaPen;
CodeProject - DrawTools(画图工具)_句柄_07    Region areaRegion;
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    // Create path which contains wide line
CodeProject - DrawTools(画图工具)_句柄_07    // for easy mouse selection
CodeProject - DrawTools(画图工具)_句柄_07    AreaPath = new GraphicsPath();
CodeProject - DrawTools(画图工具)_句柄_07    AreaPen = new Pen(Color.Black, 7);
CodeProject - DrawTools(画图工具)_句柄_07    AreaPath.AddLine(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
CodeProject - DrawTools(画图工具)_句柄_07        // startPoint and EndPoint are class members of type Point
CodeProject - DrawTools(画图工具)_句柄_07    AreaPath.Widen(AreaPen);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    // Create region from the path
CodeProject - DrawTools(画图工具)_句柄_07    AreaRegion = new Region(AreaPath);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    return AreaRegion.IsVisible(point);
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03

DrawPolygon函数使用了同样的方法,但是AreaPath包含了多边形的所有线。

 

 

 

序列化

 

GraphicList类实现了ISerializable接口,这个接口用作类对象的二进制序列化。DrawObject类有两个虚函数用来做序列化。


CodeProject - DrawTools(画图工具)_应用程序_03public virtual void SaveToStream(SerializationInfo info, int orderNumber)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    // CodeProject - DrawTools(画图工具)_应用程序_06
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03public virtual void LoadFromStream(SerializationInfo info, int orderNumber)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07  // CodeProject - DrawTools(画图工具)_应用程序_06
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03

 

这些函数在每一个继承类中都实现了。二进制文件有以下格式:

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

这样就可以在GraphicList类里写普通的序列化代码,而不需要了解序列化对象的任何细节。

 

CodeProject - DrawTools(画图工具)_应用程序_03private const string entryCount = "Count";
CodeProject - DrawTools(画图工具)_应用程序_03private const string entryType = "Type";
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03// Save list to stream
CodeProject - DrawTools(画图工具)_应用程序_03[SecurityPermissionAttribute(SecurityAction.Demand, 
CodeProject - DrawTools(画图工具)_应用程序_03                         SerializationFormatter=true)]
CodeProject - DrawTools(画图工具)_应用程序_03public virtual void GetObjectData(SerializationInfo info, 
CodeProject - DrawTools(画图工具)_应用程序_03                                     StreamingContext context)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    // number of objects
CodeProject - DrawTools(画图工具)_句柄_07    info.AddValue(entryCount, graphicsList.Count);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    int i = 0;
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    foreach ( DrawObject o in graphicsList )
CodeProject - DrawTools(画图工具)_控件_122CodeProject - DrawTools(画图工具)_序列化_123    CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07        // object type
CodeProject - DrawTools(画图工具)_句柄_07        info.AddValue(
CodeProject - DrawTools(画图工具)_句柄_07            String.Format(CultureInfo.InvariantCulture,
CodeProject - DrawTools(画图工具)_句柄_07                "{0}{1}",
CodeProject - DrawTools(画图工具)_句柄_07                entryType, i),
CodeProject - DrawTools(画图工具)_句柄_07            o.GetType().FullName);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07        // object itself
CodeProject - DrawTools(画图工具)_句柄_07        o.SaveToStream(info, i);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07        i++;
CodeProject - DrawTools(画图工具)_序列化_136    }
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03
CodeProject - DrawTools(画图工具)_应用程序_03// Load from stream
CodeProject - DrawTools(画图工具)_应用程序_03protected GraphicsList(SerializationInfo info, StreamingContext context)
CodeProject - DrawTools(画图工具)_序列化_04CodeProject - DrawTools(画图工具)_序列化_05CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07    graphicsList = new ArrayList();
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    // number of objects
CodeProject - DrawTools(画图工具)_句柄_07    int n = info.GetInt32(entryCount);
CodeProject - DrawTools(画图工具)_句柄_07    string typeName;
CodeProject - DrawTools(画图工具)_句柄_07    object drawObject;
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07    for ( int i = 0; i < n; i++ )
CodeProject - DrawTools(画图工具)_控件_122CodeProject - DrawTools(画图工具)_序列化_123    CodeProject - DrawTools(画图工具)_应用程序_06{
CodeProject - DrawTools(画图工具)_句柄_07        // object type
CodeProject - DrawTools(画图工具)_句柄_07        typeName = info.GetString(
CodeProject - DrawTools(画图工具)_句柄_07            String.Format(CultureInfo.InvariantCulture,
CodeProject - DrawTools(画图工具)_句柄_07                "{0}{1}",
CodeProject - DrawTools(画图工具)_句柄_07            entryType, i));
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07        // create object by type name using Reflection
CodeProject - DrawTools(画图工具)_句柄_07        drawObject = Assembly.GetExecutingAssembly().CreateInstance(
CodeProject - DrawTools(画图工具)_句柄_07            typeName);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07        // fill object from stream
CodeProject - DrawTools(画图工具)_句柄_07        ((DrawObject)drawObject).LoadFromStream(info, i);
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_句柄_07        graphicsList.Add(drawObject);
CodeProject - DrawTools(画图工具)_序列化_136    }
CodeProject - DrawTools(画图工具)_句柄_07
CodeProject - DrawTools(画图工具)_序列化_09}
CodeProject - DrawTools(画图工具)_应用程序_03