6.1.10 拖引线示例
这一小节中详细说明在一个图形应用程序中拖引线(rubberbanding)效果的实现,拖引线用来追踪用户在运行时绘制图形时鼠标的运动。这节中的代码来自EXAMPLES\DOC\GRAPHEX目录中的一个应用程序示例。该应用程序响应鼠标点击和拖动以在一个窗口的画布中绘制直线和形状:按下一个鼠标键开始绘制,释放该键结束绘制。
首先,用示例代码显示出如何在主窗体的表面绘制。后一个例子说明在位图上绘制。
本节包括:
· 响应鼠标。
· 在窗体对象中加入一个字段以追踪鼠标动作。
· 改善直线绘制。
1.响应鼠标
应用程序可响应鼠标动作:按下鼠标键,移动鼠标及释放鼠标键。同时也可响应一次单击(包括按下到释放的全过程),单击也可能由某种击键动作产生(例如在一个模态对话框中按下回车键)。
这部分内容包括:
· 一个鼠标事件中包含什么。
· 响应按下鼠标键操作。
· 响应释放鼠标键操作。
· 响应移动鼠标操作。
(1)一个鼠标事件中包含什么
VCL有三个鼠标事件:OnMouseDown事件、OnMouseMove事件及OnMouseUp事件。
当VCL应用程序检测到一个鼠标动作时,它调用为相应的事件定义的无论什么事件处理程序,并传递5个参数。用这些参数中包含的信息可定制对鼠标事件的响应。5个参数如表6-4所示:
大部分情况下,只需要鼠标事件处理程序中返回的坐标,但有时也需要检查Button参数以判定是哪个鼠标键引发了事件。
注意C++Builder在判定哪个鼠标键被按下时与MicrosoftWindows使用相同的标准。这样,若交换了缺省的“主”及“次”鼠标键(鼠标右键为当前的“主”鼠标键),点击主(右)键将把mbLeft作为Button参数的值。
(2)响应按下鼠标键操作
当用户按下一个鼠标键时,当前鼠标指针所指的对象发生一个OnMouseDown事件。然后对象可响应该事件。要响应按下鼠标键操作,给OnMouseDown事件分配一个事件处理程序。
VCL会为窗体中的按下鼠标事件产生一个空的处理程序:

这里可加入在按下鼠标键的位置上显示一些文本的代码,代码调用画布的TextOut方法并传入X和Y参数以显示文本。
下列代码在窗体中按下鼠标键的位置显示字符串“Here!”:
应用程序运行时,当鼠标光标在窗体中时按下鼠标键,会在按下鼠标键的位置显示字符串“Here!”。
下列代码将会把当前绘图位置设为用户按下鼠标键的位置坐标:
按下鼠标会设置当前的画笔位置,设置直线起点。要绘制到用户释放按钮处的直线,需响应释放鼠标键事件。
(3)响应释放鼠标键操作
当用户释放鼠标键时,OnMouseUp事件发生。该事件通常发送给当用户按下鼠标键时鼠标光标在其之上的对象,释放鼠标键不需要光标还在同一个对象上。这可用于,例如,画一条好像延伸到窗体边界之外的直线。要响应释放键操作,需为OnMouseUp事件定义一个处理程序。
下面是一个简单的OnMouseUp事件处理程序,它绘制一条到释放鼠标键的点的直线:
这段代码让用户通过按下、拖动和释放鼠标绘制直线。在这种情况中,在鼠标键被释放前用户看不见所绘制的直线。
(4)响应移动鼠标操作
当用户移动鼠标时,一个OnMouseMove事件周期性地发生。事件通常发送给用户按下鼠标键时鼠标光标下面的对象。这样就可在鼠标移动时通过绘制临时的直线给用户一些相关反馈。要响应移动鼠标操作,为OnMouseMove事件定义一个事件处理程序。下例当用户保持按下鼠标键时在窗体中绘制形状,这能给用户提供一些反馈。OnMouseMove事件处理程序绘制一条直线到发生OnMouseMove事件的位置: 
使用这段代码,甚至在鼠标键按下之前,在窗体中移动鼠标都会引起跟随鼠标的绘制操作。甚至在当没按下鼠标键时,鼠标移动事件也会发生。若要追踪是否有鼠标键被按下,需在窗体对象中加入一个对象字段。
2.在窗体对象中加入字段以追踪鼠标动作
要追踪鼠标键是否被按下,必须在窗体对象中加入对象字段。当把一个组件加入窗体时,C++Builder在窗体对象中增加代表该组件的字段,以便可通过其字段名引用组件。也可通过编辑窗体单元头文件中的类型声明将自己的字段加入窗体。
在下例中,窗体需要追踪用户是否按下鼠标键。为此,增加一个布尔型字段且当用户按下鼠标键时设置其值。要在对象中增加字段,需编辑对象的类型定义,在声明最末处public指令后指定字段标识符及类型。C++Builder“拥有”public指令前的任何声明:那是它放置代表控件的字段和响应事件的方法的地方。
下列代码在窗体对象的声明中将一个名为Drawing的bool类型的字段加入窗体。同时还增加了用以存储点的两个POINT类型的字段Origin和MovePt。
现在可用Drawing字段追踪是否开始绘制操作,当用户按下鼠标键时将其设为true,当用户释放鼠标键时设为false:
然后可修改OnMouseMove事件处理程序,只在Drawing为true时进行绘制:
这使得绘制仅发生在按下鼠标键事件和释放事件之间,同时得到追踪鼠标动作的一条随意绘制的线而不是一条直线。问题是每次你移动鼠标,鼠标移动事件处理程序调用LineTo,它移动画笔位置,这样当释放鼠标键,你会失去直线应当开始的点。
3.改善直线绘制
有了可追踪各种点的字段,就可改善应用程序的直线绘制。
(1)追踪起点
当绘制直线时,可用Origin字段追踪直线的起点。Origin应被设为按下鼠标键事件的发生点,释放鼠标键事件处理程序就能使用它来设置直线的起点,
代码如下:
代码中的这些变化可让应用程序重新绘制最后的直线,但不绘制任何中间的动作,也就是应用程序还不支持“拖引线”。
(2)追踪移动
当前的OnMouseMove事件处理程序的问题是它从刚才的鼠标位置绘制直线到当前的鼠标位置,而不是从起始点。可通过移动绘制位置到起始点来改正上述问题,然后绘制到当前点:

上述代码追踪当前的鼠标位置,但是中间的线没有消除,因此几乎看不见最后的线。在此例中还需通过追踪先前的线的位置以在绘制下一条线前消除先前的线。这可用MovePt字段来完成。
MovePt必须被设为每条中间的线的终点,以便可用MovePt和Origin来消除先前的线:

现在就在绘制直线时得到了“拖引线”的效果。通过将画笔的模式设为pmNotXor,可把绘制的直线与背景像素相结合。当消除线后确实将像素设回到它们原来的方式。而在绘制线后通过将画笔模式设回pmCopy(缺省值),可确保画笔准备好在释放鼠标键时做最后的绘制操作。 |