原作者:Alex Farber
原文:http://www.codeproject.com/csharp/DrawTools.asp(源代码请参见原文)


介绍

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

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

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

 


 

  • 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按钮的消息句柄并没有改变窗体控件的状态,它只是在一些变量中保存了当前的选择。空闲的消息句柄选择激活的工具,取消未激活工具的选择。


private void Form1_Load(object sender, System.EventArgs e)
{
    
// Submit to Idle event to set controls state at idle time
    Application.Idle += new EventHandler(Application_Idle);
}


private void Application_Idle(object sender, EventArgs e)
{
    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 类有一个叫HitTest的虚拟函数,用来侦测是否Point属于图形对象。

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

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

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

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

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函数使用了同样的方法,但是AreaPath包含了多边形的所有线。

序列化

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


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


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

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

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

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

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);
    }


}

posted on 2007-03-17 14:07  大口仔  阅读(1581)  评论(0编辑  收藏  举报

使用Live Messenger联系我
关闭