您的位置:首页 > 编程语言 > Delphi

delphi 基础原理--什么是事件/自定义事件

2013-07-01 00:00 621 查看


Events are special properties linked to code that get executed whenever a particular action occurs. This article discusses how events are generated and how you can define your own event properties for your custom Delphi components.

This article is excerpted from
Delphi 6 Developer's Guide, by Xavier Pacheco and Steve Teixeira (Sams, 2001, ISBN 0-672-32115-7).

Where Do Events Come From?

The general definition of an event is basically any type of occurrence that might result from user interaction, the system, or from code logic. The event is linked to some code that responds to that occurrence. The linkage of the event to code that responds to an event is called an event propertyand is provided in the form of a method pointer. The method to which an event property points is called an event handler.
For example, when the user clicks the mouse button, a WM_MOUSEDOWN message is sent to the Win32 system. Win32 passes that message to the control for which the message was intended. This control can then respond to the message. The control can respond to this event by first checking to see whether there is any code to execute. It does this by checking to see whether the event property points to any code. If so, it executes that code, or rather, the event handler.
The OnClick event is just one of the standard event properties defined by Delphi. OnClick and other event properties each have a corresponding event-dispatching method. This method is typically a protected method of the component to which it belongs. This method performs the logic to determine whether the event property refers to any code provided by the user of the component. For the OnClick property, this would be the Click() method. Both the OnClick property and theClick() method are defined by TControl as follows:
TControl = class(TComponent)
private
FOnClick: TNotifyEvent;
protected
procedure Click; dynamic;
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
Here is the TControl.Click() method:
procedure TControl.Click;
begin
if Assigned(FOnClick) then FOnClick(Self);
end;
One bit of essential information that you must understand is that event properties are nothing more than method pointers. Notice that the FOnClick property is defined to be a TNotifyEvent.TNotifyEvent is defined as follows:
TNotifyEvent = procedure(Sender: TObject) of object;
This says that TNotifyEvent is a procedure that takes one parameter, Sender, which is of the typeTObject. The directive, of object, is what makes this procedure become a method. This means that an additional implicit parameter that you don't see in the parameter list also gets passed to this procedure. This is the Self parameter that refers to the object to which this method belongs. When the Click() method of a component is called, it checks to see if FOnClick actually points to a method, and if so, calls that method.
As a component writer, you write all the code that defines your event, your event property, and your dispatching methods. The component user will provide the event handler when using your component. Your event-dispatching method will check to see whether the user has assigned any code to your event property and then execute it when code exists.
Event handlers are assigned to event properties either at runtime or at design time. In the following section, we show you how to create your own events, event properties, and dispatching methods.

Defining Event Properties

Before you define an event property, you need to determine whether you need a special event type. It helps to be familiar with the common event properties that exist in the Delphi VCL. Most of the time, you'll be able to have your component descend from one of the existing components and just use its event properties, or you might have to surface a protected event property. If you determine that none of the existing events meets your need, you can define your own.
As an example, consider the following scenario. Suppose you want a component containing an event that gets called every half minute based on the system clock. That is, it gets invoked on the minute and on the half minute. Well, you can certainly use a TTimer component to check the system time and then perform some action whenever the time is at the minute or half minute. However, you might want to incorporate this code into your own component and then make that component available to your users so that all they have to do is add code to your OnHalfMinuteevent.
The TddgHalfMinute component shown in Listing 1 illustrates how you would design such a component. More importantly, it shows how you would go about creating your own event type.

Listing 1—TddgHalfMinute—Event Creation

unit halfmin;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls;

type
{ Define a procedure for the event handler. The event property will
be of this procedure type. This type will take two parameters, the
object that invoked the event and a TDateTime value to represent
the time that the event occurred. For our component this will be
every half-minute. }
TTimeEvent = procedure(Sender: TObject; TheTime: TDateTime) of object;

TddgHalfMinute = class(TComponent)
private
FTimer: TTimer;
{ Define a storage field to point to the user's event handler.
The user's event handler must be of the procedural type
TTimeEvent. }
FOnHalfMinute: TTimeEvent;
FOldSecond, FSecond: Word; // Variables used in the code
{ Define a procedure, FTimerTimer that will be assigned to
FTimer.OnClick. This procedure must be of the type TNotifyEvent
which is the type of TTimer.OnClick. }
procedure FTimerTimer(Sender: TObject);
protected
{ Define the dispatching method for the OnHalfMinute event. }
procedure DoHalfMinute(TheTime: TDateTime); dynamic;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
// Define the actual property that will show in the Object Inspector
property OnHalfMinute: TTimeEvent read FOnHalfMinute write FOnHalfMinute;
end;

implementation

constructor TddgHalfMinute.Create(AOwner: TComponent);
{ The Create constructor, creates the TTimer instanced for FTimer. It
then sets up the various properties of FTimer, including its OnTimer
event handler which is TddgHalfMinute's FTimerTimer() method. Notice
that FTimer.Enabled is set to true only if the component is running
and not while the component is in design mode. }
begin
inherited Create(AOwner);
// If the component is in design mode, do not enable FTimer.
if not (csDesigning in ComponentState) then
begin
FTimer := TTimer.Create(self);
FTimer.Enabled := True;
// Set up the other properties, including the FTimer.OnTimer event handler
FTimer.Interval := 500;
FTimer.OnTimer := FTimerTimer;
end;
end;

destructor TddgHalfMinute.Destroy;
begin
FTimer.Free;
inherited Destroy;
end;

procedure TddgHalfMinute.FTimerTimer(Sender: TObject);
{ This method serves as the FTimer.OnTimer event handler and is assigned
to FTimer.OnTimer at run-time in TddgHalfMinute's constructor.

This method gets the system time, and then determines whether or not
the time is on the minute, or on the half-minute. If either of these
conditions are true, it calls the OnHalfMinute dispatching method,
DoHalfMinute. }
var
DT: TDateTime;
Temp: Word;
begin
DT := Now; // Get the system time.
FOldSecond := FSecond; // Save the old second.
// Get the time values, needed is the second value
DecodeTime(DT, Temp, Temp, FSecond, Temp);

{ If not the same second when this method was last called, and if
it is a half minute, call DoOnHalfMinute. }
if FSecond <> FOldSecond then
if ((FSecond = 30) or (FSecond = 0)) then
DoHalfMinute(DT)
end;

procedure TddgHalfMinute.DoHalfMinute(TheTime: TDateTime);
{ This method is the dispatching method for the OnHalfMinute event.
it checks to see if the user of the component has attached an
event handler to OnHalfMinute and if so, calls that code. }
begin
if Assigned(FOnHalfMinute) then
FOnHalfMinute(Self, TheTime);
end;

end.
When creating your own events, you must determine what information you want to provide to users of your component as a parameter in the event handler. For example, when you create an event handler for the TEdit.OnKeyPress event, your event handler looks like the following code:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
end;
Not only do you get a reference to the object that caused the event, but you also get a Charparameter specifying the key that was pressed. Deep in the Delphi VCL, this event occurred as a result of a WM_CHAR Win32 message that drags along some additional information relating to the key pressed. Delphi takes care of extracting the necessary data and making it available to component users as event handler parameters. One of the nice things about the whole scheme is that it enables component writers to take information that might be somewhat complex to understand and make it available to component users in a much more understandable and easy-to-use format.
Notice the var parameter in the preceding Edit1KeyPress() method. You might be wondering why this method wasn't declared as a function that returns a Char type instead of a procedure. Although method types can be functions, you shouldn't declare events as functions because it will introduce ambiguity; when you refer to a method pointer that is a function, you can't know whether you're referring to the function result or to the function pointer value itself. By the way, one function event in the VCL slipped past the developers from the Delphi 1 days, and now it must remain. This event is the TApplication.OnHelp event.
Looking at Listing 1, you'll see that we've defined the procedure type TOnHalfMinute as this:
TTimeEvent = procedure(Sender: TObject; TheTime: TDateTime) of object;
This procedure type defines the procedure type for the OnHalfMinute event handler. Here, we decided that we want the user to have a reference to the object causing the event to occur and theTDateTime value of when the event occurred.
The FOnHalfMinute storage field is the reference to the user's event handler and is surfaced to the Object Inspector at design time through the OnHalfMinute property.
The basic functionality of the component uses a TTimer object to check the seconds value every half second. If the seconds value is 0 or 30, it invokes the DoHalfMinute() method, which is responsible for checking for the existence of an event handler and then calling it. Much of this is explained in the code's comments, which you should read over.
After installing this component to Delphi's Component Palette, you can place the component on the form and add the following event handler to the OnHalfMinute event:
procedure TForm1.ddgHalfMinuteHalfMinute(Sender: TObject; TheTime: TDateTime);
begin
ShowMessage('The Time is '+TimeToStr(TheTime));
end;
This should illustrate how your newly defined event type becomes an event handler.

原文出处:http://www.informit.com/articles/article.aspx?p=26651
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  delphi 事件