荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Peter (小飞侠), 信区: Program
标  题: Delphi自定义部件(五)
发信站: BBS 荔园晨风站 (Fri Jan 22 13:46:51 1999), 转信


    3. 定义自己的事件
  定义全新的事件的情况是很少见的。只有当部件的行为完全不同于任何其它事件才需
要定义新事件。定义新事件一般包含三个步骤:
    ● 触发事件
    ● 定义处理过程类型
    ● 声明事件
    ● 调用事件

    ⑴ 触发事件
  定义自己的事件要遇到的第一个关键是:当使用标准事件时你不需要考虑由什么触发
事件。对某些事件,问题是显然的。例如:一个MouseDown事件是在用户按下鼠标的左键
时发生,Windows给应用发送WM_LBUTTONDOWN消息。接到消息后,一个部件调用
它的MouseDown方法,它依次调用用户的OnMouseDown事件处理过程代码。但是有些事
件却不是那么可以描述清楚的。例如:滚行杠有一个OnChange事件,可被各种情况触发,
包括按键、鼠标点按或其它按制中的改变。当定义事件时,你必须使各种情况的发生调用
正确的事件。
  这里有TControl处理WM_LBUTTONDOWN消息的方法,DoMouseDown是私有的实
现方法,它提供了一般的处理左、右和中按钮的方法,并将Windows消息的参数转换为
MouseDown方法的值。

    type
      TControl = class(TComponent)
      private
        FOnMouseDown: TMouseEvent;
        procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;
                                  Shift: TShiftState);
        procedure WMLButtonDown(var Message: TWMLButtonDown);
                                    message M_LBUTTONDOWN;
      protected
      procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
                             X, Y: Integer); dynamic;
    end;

    procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
    begin
      if Assigned(FOnMouseDown) then
        FOnMouseDown(Self, Button, Shift, X, Y);              { 调用事件处理过程
 }
    end;

    procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton;
                                        Shift: ShiftState);
    begin
      with Message do
      MouseDown(Button, KeysToShiftState(Keys) + Shift, XPos, YPos); { 调用动态
方法 }
    end;

    procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
    begin
      inherited; { perform default handling }
      if csCaptureMouse in ControlStyle then
        MouseCapture := True;
      if csClickEvents in ControlStyle then
        Include(FControlState, csClicked);
      DoMouseDown(Message, mbLeft, []); { 调用常规的mouse-down 方法 }
    end;

  当两种事情-状态变化和用户交互—发生时,处理机制是相同的,但过程稍微不同。
用户交互事件将总是由Windows消息触发。状态改变事件也与Windows消息有关,但它们
也可由属性变化或其它代码产生。你拥有对自定义事件触发的完全控制。
    ⑵ 定义处理过程类型
  一旦你决定产生事件,就要定义事件如何被处理,这就是要决定事件处理过程的类型。
在大多数情况下,定义的事件处理过程的类型是简单的通知类型(TNotifyEvent)和已定
义的事件类型。
  通知事件只是告诉你特定的事件发生了,而没有描述什么时候和什么地方。通知事件
使用时只带一个TObject类型的参数,该参数是Sender。然而所有通知事件的处理过程都
知道是什么样的事件发生和发生在那个部件。例如:Click事件是通知类型。当编写Click
事件的处理过程时,你知道的是Click事件发生和哪个部件被点按了。通知事件是单向过
程。没有提供反馈机制。
  在某些情况下,只知道什么事件发生和发生在那个部件是不够的。如果按键事件发生,
事件处理过程往往要知道用户按了哪个键。在这种情况下,需要事件处理过程包含有关事
件的必要信息的参数。如果事件产生是为了响应消息,那么传递给事件的参数最好是直接
来自消息参数。
  因为所有事件处理过程都是过程,所以从事件处理过程中返回信息的唯一方法是通过
var参数。自定义部件可以用这些信息决定在用户事件处理过程执行后是否和怎样处理事
件。
  例如,所有的击键事件(OnKeyDown、OnKeyUp和OnKeyPressed)通过名为key的var
参数传递键值。为了使应用程序看见包含在事件中的不同的键,事件处理过程可以改变key
变量值。
    ⑶ 声明事件
  一旦你决定了事件处理过程的类型,你就要准备声明事件的方法指针和属性。为了让
用户易于理解事件的功能,应当给事件一个有意义的名字,而且还要与部件中相似的属性
的名称保持一致。
  Delphi中所有标准事件的名称都以“On”开头。这只是出于方便,编译器并不强制
它。Object Inspector是看属性类型来决定属性是否是事件,所有的方法指针属性都被看作
事件,并出现在事件页中。
    ⑷ 调用事件
  一般说来,最好将调用集中在事件上。就是说在部件中创建一个虚方法来调用用户的
事件处理过程和提供任何缺省处理。当调用事件时,应考虑以下两点:
  ● 必须允许空事件
    ● 用户能覆盖缺省处理

    不能允许使空事件处理过程产生错误的情况出现。就是说,自定义部件的正常功能不
能依赖来自用户事件处理过程的响应。实际上,空事件处理过程应当产生与无事件处理过
程一样的结果。

  部件不应当要求用户以特殊方式使用它们。既然一个空事件处理过程应当与无事件处
理过程一样动作,那么调用用户事件处理过程的代码应当象这样:

  if Assigned(OnClick) then OnClick(Self);
       { 执行缺省处理 }

    而不应该有这样的代码:

    if Assigned(OnClick) then
      OnClick(Self)
    else
        …;  { 执行缺省处理 }

  对于某些种类的事件,用户可能想取代缺省处理甚至删除所有的响应。为支持用户实
现这种功能,你需要传递var参数给事件处理过程,并在事件处理过程返回时检测某个值。
空事件处理过程与无事件处理过程有相同作用。因为空事件处理过程不会改变任何var参数
值。所以缺省处理总是在调用空事件处理过程后发生。
  例如在处理Key-Press事件,用户可以通过将var参数key的值设置为空字符(#0)来压
制部件的缺省处理,代码如下:

  if Assigned(OnkeyPress) then OnkeyPress(Self key);
    if key <> #0 then   { 执行缺省处理 } ;

    实际的代码将与这稍有不同,因为它只处理窗口消息,但处理逻辑是相同的。在缺省
情况下,部件先调用任何用户赋予的事件处理过程,然后执行标准处理。如果用户的事件
处理过程将key设为空,则部件跳过缺省处理。

--
※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.1.3]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店