⑵ 定义处理过程类型
一旦你决定产生事件就要定义事件如何被处理这就是要决定事件处理过程的类型在大多数情况下定义的事件处理过程的类型是简单的通知类型(TNotifyEvent)和已定义的事件类型
通知事件只是告诉你特定的事件发生了而没有描述什么时候和什么地方通知事件使用时只带一个TObject类型的参数该参数是Sender然而所有通知事件的处理过程都知道是什么样的事件发生和发生在那个部件例如Click事件是通知类型当编写Click事件的处理过程时你知道的是Click事件发生和哪个部件被点按了通知事件是单向过程没有提供反馈机制
在某些情况下只知道什么事件发生和发生在那个部件是不够的如果按键事件发生事件处理过程往往要知道用户按了哪个键在这种情况下需要事件处理过程包含有关事件的必要信息的参数如果事件产生是为了响应消息那么传递给事件的参数最好是直接来自消息参数
因为所有事件处理过程都是过程所以从事件处理过程中返回信息的唯一方法是通过var参数自定义部件可以用这些信息决定在用户事件处理过程执行后是否和怎样处理事件
例如所有的击键事件(OnKeyDownOnKeyUp和OnKeyPressed)通过名为key的var参数传递键值为了使应用程序看见包含在事件中的不同的键事件处理过程可以改变key变量值
⑶ 声明事件
一旦你决定了事件处理过程的类型你就要准备声明事件的方法指针和属性为了让用户易于理解事件的功能应当给事件一个有意义的名字而且还要与部件中相似的属性的名称保持一致
Delphi中所有标准事件的名称都以On开头这只是出于方便编译器并不强制它Object Inspector是看属性类型来决定属性是否是事件所有的方法指针属性都被看作事件并出现在事件页中
⑷ 调用事件
一般说来最好将调用集中在事件上就是说在部件中创建一个虚方法来调用用户的事件处理过程和提供任何缺省处理当调用事件时应考虑以下两点
● 必须允许空事件
● 用户能覆盖缺省处理
不能允许使空事件处理过程产生错误的情况出现就是说自定义部件的正常功能不能依赖来自用户事件处理过程的响应实际上空事件处理过程应当产生与无事件处理过程一样的结果
部件不应当要求用户以特殊方式使用它们既然一个空事件处理过程应当与无事件处理过程一样动作那么调用用户事件处理过程的代码应当象这样
if Assigned(OnClick) then OnClick(Self)
{ 执行缺省处理 }
而不应该有这样的代码
if Assigned(OnClick) then
OnClick(Self)
else
… { 执行缺省处理 }
对于某些种类的事件用户可能想取代缺省处理甚至删除所有的响应为支持用户实现这种功能你需要传递var参数给事件处理过程并在事件处理过程返回时检测某个值空事件处理过程与无事件处理过程有相同作用因为空事件处理过程不会改变任何var参数值所以缺省处理总是在调用空事件处理过程后发生
例如在处理KeyPress事件用户可以通过将var参数key的值设置为空字符(#)来压制部件的缺省处理代码如下
if Assigned(OnkeyPress) then OnkeyPress(Self key)
if key <> # then { 执行缺省处理 } ;
实际的代码将与这稍有不同因为它只处理窗口消息但处理逻辑是相同的在缺省情况下部件先调用任何用户赋予的事件处理过程然后执行标准处理如果用户的事件处理过程将key设为空则部件跳过缺省处理
处理消息
在传统Windows编程中一个很关键的方面是处理Windows发送给应用程序的消息Delphi已经帮你处理了大多数的普通消息但是在创建部件的过程中有可能Delphi没有处理方法得由自己处理消息也可能创建了新的消息需要处理它们
学习掌握Delphi的消息处理要掌握以下三个方面
● 理解消息处理系统
● 修改(改变)消息处理方法
● 建立新的消息处理方法
理解消息处理系统
所有的Delphi对象内部具有处理消息的机制如调用消息处理方法或消息处理过程消息处理的基本思想是对象接收某种消息并派送它们这是通过调用与接收的消息相应的方法来实现的如果没有相应于消息的指定的方法那就调用缺省处理下面的图解表示消息派送系统
Delphi部件库定义了将所有Windows消息(包括用户自定义消息)直接转换到对象方法调用的消息派送系统一般没有必要改变这种消息派送系统只要建立消息处理方法
⑴Windows消息中有什么?
Windows消息是包含若干有用的域的数据记录记录中最重要的是一个整型大小的值该值标识消息Windows定义了大量的消息库单元Messages声明了所有消息的标识消息中其它的有用信息包括两个域参数和结果域两个参数分别是位和位的Windows代码总是以wParam和lParam来引用它们
最初Windows程序员不得不记住包含的每一个参数现在微软公司已经命名了这参数这样理解伴随这些消息的信息就更简单了例如WM_KEYDOWN消息的参数被称为vkey和keydata这就比wParam和lParam给出了更多的描述信息
Delphi为不同类型的消息定义了指定的记录类型如鼠标消息在long参数中传递鼠标事件的xy座标一个在高字一个在低字使用鼠标消息记录你不需要自己关心哪个字是哪个座标因为引用这些参数时通过名子Xpos和Ypos取代了lParamLo和lParamHi
⑵ 派送方法
当应用程序创建窗口时在Windows Kernel中注册了一个窗口过程窗口过程是处理窗口消息的函数传统上窗口过程包括了Case表达式表达式的每个入口是窗口要处理的每一条消息当你每次创建窗口时必须建立完整的窗口过程
Delphi在下列三方面简化了消息派送
● 每个部件继承了完整的消息派送系统
● 派送系统具有缺省处理用户只需定义想响应的消息的处理方法
● 可以修改消息处理的一部分依靠继承的方法完成大多数处理
这种消息派送系统的最大优点是用户能在任何时候安全地发送任何消息给任何部件如果部件没有为该消息定义处理方法那缺省处理方法会解决这个问题通常是忽略它
Delphi为应用程序每种类型的部件注册了名为MainWndProc的方法作为窗口过程MainWndProc包含了异常处理块它完成从Windows到名为WndProc的虚方法传送消息记录并且通过调用应用程序对象的HandleException方法处理异常
MainWndProc是静态方法没有包含任何消息的指定处理方法定制过程发生在WndProc中因为每个部件类型都能覆盖该方法以适合特定的需要
WndProc方法为每个影响它们处理的任何条件进行检查以捕捉不要的消息例如当被拖动时部件忽略键盘事件因此TWinControl的WndProc只在没有拖动时传送键盘事件最后WndProc调用Dispatch方法该方法是从TObject继承来的静态方法决定什么方法来处理消息
Dispatch使用消息记录的Msg域来决定怎样派送特定消息如果部件已经给该消息定义了处理方法则Dispatch调用该方法反之Dispatch调用缺省处理方法
改变消息处理方法
在改变自定义部件的消息处理方法之前先要弄清楚你真正想要做什么Delphi将大多数的Windows消息转换成部件编写者和部件用户都能处理的事件一般来说你应当改变事件处理行为而不是改变消息处理行为
为了改变消息处理行为要覆盖消息处理方法也能提供捕获消息防止部件处理该消息
⑴ 覆盖处理方法
为了改变部件处理特定消息的方法要覆盖那个消息的处理方法如果部件不处理该消息你就需要声明新的消息处理方法
为了覆盖消息处理方法要在部件中以相同的消息索引声明新的方法不要使用override指令你必须使用Message指令和相应的消息索引
例如为了覆盖一个处理WM_PAINT消息的方法你要重声明WMPaint方法
type
TMyComponent=class(…)
procedure WMPaint(var Message: TWMPaint) message WM_PAINT;
end;
⑵ 使用消息参数
在消息处理方法内部自定义部件访问消息记录的所有参数因为消息总是var参数如果需要的话事件处理过程可以改变参数的值Result域是经常改变的参数Result是Windows文档中所指的消息的返回值由SendMessage返回
因为消息处理方法的消息参数的类型随着被处理的消息的变化而变化所以应当参考Windows消息文档中的参数的名字和含义如果出于某种原因要使用旧风格的消息参数(wParamlParam)可以配合通用类型TMessage来决定Message
⑶ 捕获消息
在某种情况下你可能希望自定义部件能忽略某种消息就是说阻止部件将该消息派送给它的处理方法为了那样来捕获消息可以覆盖虚方法WndProc
WndProc方法在将消息传给Dispatch方法前屏蔽该消息它依次决定哪一个方法来处理消息通过覆盖WndProc部件得到了派送消息之前过滤它们的机会
通常象下面这样覆盖WndProc:
procedure TMyControlWndProc(var Message: TMessage)
begin
{ 决定是否继续处理过程 }
inherited WndProc (Message)
end;
下面的代码是TControl的WndProc的一部分TControl定义整个范围内的鼠标消息当用户拖动和放置控制时它们将被滤过
procedure TControl WndProc(var Message:TMessage)
begin
if (MessageMsg >= WM_MOVSEFIRST) and
(MessageMsg <= WM_MOUSELAST) then
if Dragging then
DragMouseMsg(TWMMOUSE(Message)) { 处理拖动 }
else
… { 正常处理其它 }
… { 否则正常处理 }
end;
返回目录DELPHI基础教程
编辑推荐
Java程序设计培训视频教程
JEE高级框架实战培训视频教程
Visual C++音频/视频技术开发与实战
Oracle索引技术
ORACLEG数据库开发优化指南
Java程序性能优化让你的Java程序更快更稳定
C嵌入式编程设计模式
Android游戏开发实践指南
[] [] [] []