Icon Events

Events are declared just like properties in classes, but read/write a method reference instead of a string, integer, etc. value. A method reference is similar to a class instance reference. But, instead of referring to only a class instance, a method reference contains both a class instance reference and a reference to a method declaration. Method references are useful because they can be called just like a normal method, but can also be assigned to variables and passed as parameters to other methods, procedures, or functions. This allows you to assign specific behaviors to the event properties of a class, swap such behaviors in and out with other behaviors, or assign no behavior at all.

Before an event can be declared, the event's method reference type must be declared. A method reference type is declared as follows:

<Type Name> = function/procedure ([<Parameters>])[: Type Name>] of object;

The procedure or function declaration is identical to a normal procedure or function prototype, except that a procedure or function name is not specified.

For example, consider the following method reference type and class/event declarations:

interface

   TStartEvent = procedure (StartingVehicle: TVehicle) of object;

   TVehicle = class
      private
         FOnStart: TStartEvent;
      public
         property OnStart: TStartEvent read FOnStart write FOnStart;
         procedure Start;
      end;

implementation

procedure TVehicle.Start;
begin
   if Assigned(FOnStart) then
      FOnStart(Self);
end;

Defining Event Handlers
Once an event is declared as a property in a class, it is still not very useful until the event is assigned an actual method reference. This type of method reference is referred to as an event handler. For example, the following code creates an instance of the TVehicle class and assigns an OnStart event handler:

interface

   TGarage = class
      private
         FVehicle: TVehicle;         
      protected
         procedure DoVehicleStart(StartingVehicle: TVehicle);
      public
         constructor Create; override;
         destructor Destroy; override;
      end;

implementation

constructor TGarage.Create;
begin
   inherited Create;
   FVehicle:=TVehicle.Create;
   FVehicle.OnStart:=DoVehicleStart;
   FVehicle.Start;
end;

destructor TGarage.Destroy;
begin
   FVehicle.Free;
   inherited Destroy;
end;

procedure TGarage.DoVehicleStart(StartingVehicle: TVehicle);
begin
   ShowMessage('Vehicle has been started');
end;

Information Event handlers are always called using a reference to the class instance that contains the event handler. So, in the above example when the DoVehicleStart event handler is called, it will be called using the TGarage class instance that was in scope when the OnStart property was assigned.

Method reference assignments and parameters must be type-compatible with the declared type of the target variable or parameter. To illustrate this concept, suppose that the above DoVehicleStart event handler was declared as follows:

TGarage = class
   private
      FVehicle: TVehicle;         
   protected
      procedure DoVehicleStart;
   public
      constructor Create; override;
      destructor Destroy; override;
   end;

This would result in a compiler error on the source line where the event handler is assigned:

constructor TGarage.Create;
begin
   inherited Create;
   FVehicle:=TVehicle.Create;
   FVehicle.OnStart:=DoVehicleStart;  // Compiler error here !!!
   FVehicle.Start;
end;

The reason for the compiler error is simple: the TStartEvent type of the OnStart event is declared with a TVehicle parameter, and the DoVehicleStart method is not declared with any parameters.

Clearing Event Handlers
Just as you can attach a behavior to the event property of a class in the form of an event handler, you can also remove that behavior by assigning nil to the event property:

constructor TGarage.Create;
begin
   inherited Create;
   FVehicle:=TVehicle.Create;
   FVehicle.OnStart:=DoVehicleStart;
   FVehicle.Start;
   FVehicle.OnStart:=nil; // Clear event handler
end;

Warning Because the method reference variables that are used with event properties can be nil, you should always check for this before trying to call such method reference variables. The best way to do so is by using the Assigned function, as illustrated in the TVehicle.Start method implementation:

procedure TVehicle.Start;
begin
   if Assigned(FOnStart) then
      FOnStart(Self);
end;

Default Events
Components and controls that will be used in the component library can have a default event property. A default event property is the event handler that will be created if the user double-clicks on a component or control in the designer in the IDE. For example, the following TVehicle class has been slightly modified from the above example. It is now a descendant of the TComponent class so that it can be installed into the component library, and now defines the OnStart event as the default event in the published section of the class:

interface

   TStartEvent = procedure (StartingVehicle: TVehicle) of object;

   TVehicle = class(TComponent)
      private
         FOnStart: TStartEvent;
      public
         procedure Start;
      published
         property OnStart: TStartEvent read FOnStart write FOnStart; default;
      end;

implementation

procedure TVehicle.Start;
begin
   if Assigned(FOnStart) then
      FOnStart(Self);
end;

A default event is only used in the designer if the event property is published. Please see the Scope topic for more information on published properties.
Image