字符中的制表符比较特殊,因为它的宽度是不定的,而是根据它在文档视图中的位置而定的,因此在TextChar上在派生TextCharTab来转变处理这种情况,它新增了RefreshTabWidth方法,来根据对象在视图区域中的左端位置计算字符宽度。在此处我认定一个制表符步长等于四个下画线字符的宽度,制表符的右端坐标必须是制表符步长的自然数倍,因此根据制表符的位置来进行取模操作和其他操作就可以计算制表符的宽度。
为了表示段落而定义了段落对象TextParagraph,该对象不是容器对象,保存了段落对齐方式的信息,该元素的显示样式类似于Word中的段落符(硬回车)的样式。
还定义了行结束对象TextLineEnd,该对象模拟了Word的分行符(软回车)。
可以定义图片对象,经过对Word处理文档的行为观察,可以发现在Word文档中插入的图片和OLE对象特性很相似,因此为了考虑文本编辑器的可扩展性,首先在TextElement的基础派生出TextObject抽象类,该抽象类表示一个在文档中的对象,该对象由其派生的类决定。
在TextObject对象派生出TextImage表示一个图片对象,该对象重写了RefreshView方法,用于在绘图输出对象上绘制一个图片。还重载了FromXML和ToXML方法来和XML节点交换数据,可以设计将图片二进制数据以Base64格式保存为XML节点下。
此外还可以根据应用的需要从TextObject对象上派生其他的类型,比如直接读取数据库在界面上绘制曲线图等等,此时文档中的该对象可以动态的展示系统中最新的数据。
可以观察到Word中的对象(包括图片)可以改变大小,当用鼠标点击图片对象时,图片四个角和四个边的中点上会显示8个小点。这些小点我称为控制点。用鼠标拖拽这8个点可以动态的改变对象的大小。其实在很多类型的程序中可以碰到这8控制点,例如在VS.NET的窗体设计器中,当前的控制周围就有这8个控制点。关于如何实现这8个控制点也是有一套的。
控制点可以分为内控制点和外控制点两种类型,我们对这8个点进行从0到7的编号。当鼠标光标移动到这8个控制点上方时需要设置为不同的光标样式。
内控制点
┌─────────────────┐
│■0 1■ 2■│
│ │
│ │
│ │
│ │
│■7 3■│
│ │
│ │
│ │
│ │
│■6 5■ 4■│
└─────────────────┘
外控制点
■ ■ ■
┌────────────────┐
│0 1 2│
│ │
│ │
│ │
│ │
■│7 3│■
│ │
│ │
│ │
│ │
│6 5 4 │
└────────────────┘
■ ■ ■
控制点上鼠标光标如下
西北-东南 SizeNWSE 南北 SizeNS 东北-西南 SizeNESW
■ ■ ■
┌────────────────┐
│0 1 2│
│ │
│ │
│ │
│ │
■│7 西-南 SizeWE 3│■ 西-南 SizeWE
│ │
│ │
│ │
│ │
│6 5 4 │
└────────────────┘
■ ■ ■
东北-西南 SizeNESW 南北 SizeNS 西北-东南 SizeNWSE
根据上图所示,已知主矩形,控制点的类型(是内控制点还是外控制点)和控制点的宽度可以计算出所有的控制点的位置。可以编一个例程,输入3个参数,主矩形区域的Rectangle结构体,是否是内控制点(不是内控制点就是外控制点)和控制点的宽度,该例程计算所有控制点的位置,然后返回一个包含8个Rectangle的数组,该数组就是0到7号的控制矩形的位置和大小。
TextObject对象显示后就应该知道自己在视图区域中的位置,当它相应鼠标移动消息时,就可以根据鼠标光标位置和8个控制矩形进行比较,若鼠标光标在某个控制矩形中时就要通知文本编辑器改变鼠标光标的样式。
一般的控制点被画成一个矩形方框,控制点也被画成两种类型,一种是填充色为深色(蓝色或黑色)和白色边框,另一种是深色边框并填充白色。可以观察VS.NET窗体设计器,可以在设计器中选择多个控制,其中有一个控件的控制点为填充色为蓝色和白色边框的,该控制为当前控件。而其他选择的控件的控制点为蓝色边框并填充白色,这些控件为选择控件。在文本编辑器中没有这种情况,因此在此可以使用内控制点方式,控制点用黑色填充,边框白色。
当鼠标在控制点上进行拖拽操作就应当可以动态的修改对象的大小,以前我是如此实现的
- 在鼠标按键按下事件处理(HandleMouseDown)中,若鼠标光标在某个控制点上则设置一个鼠标按键按下标记变量,并记下鼠标光标位置,然后退出事件处理
- 在鼠标移动事件中(HandleMouseMove),若设置了鼠标按键按下标记变量,则根据当前鼠标光标位置和上一次鼠标光标的位置之差就是鼠标光标移动的距离,该距离的水平分量和垂直分量就是对象宽度和高度的改变量,此时可以使用库函数System.Windows.Forms.ControlPaint.DrawReversibleFrame在界面上绘制一个虚线框,当鼠标移动时不断的调用该库函数,这样实现了所谓的“橡皮筋”操作
- 在鼠标按键松开事件(HandleMouseDown)处理中,根据鼠标光标的当前位置和以前记下的鼠标按键按下时的鼠标光标位置计算两者之差,这样就是整个鼠标拖拽操作中鼠标光标移动的距离,程序就可以依据该距离来改变对象的大小
经过一些编程实践,发现该操作比较麻烦,需要编写不少代码,而且代码分散在3个事件处理过程中,多了一些全局变量,很难写出一个通用例程到处调用,经过分析,将这种处理模式改掉了。其实一般的程序正在进行鼠标拖拽操作时,用户是不可能同时进行其他操作(不如边鼠标拖拽边打字),而且进行”橡皮筋“操作时程序用户界面无需重新绘制,这样可以认为进行鼠标拖拽时应用程序应用程序只处理鼠标移动消息和鼠标松开消息而不进行任何其他操作,为了编程简单,甚至连重绘界面的操作也不处理了,因此可以编一个通用例程来处理整个的鼠标拖拽来实现“橡皮筋”操作,该函数处理过程为