RAD Studio (Common)
ContentsIndex
PreviousUpNext
Events

This topic describes the following material:

  • Event properties and event handlers
  • Triggering multiple event handlers
  • Multicast events (.NET)

An event links an occurrence in the system with the code that responds to that occurrence. The occurrence triggers the execution of a procedure called an event handler. The event handler performs the tasks that are required in response to the occurrence. Events allow the behavior of a component to be customized at design-time or at runtime. To change the behavior of the component, replace the event handler with a custom event handler that will have the desired behavior.

Components that are written in Delphi use properties to indicate the event handler that will be executed when the event occurs. By convention, the name of an event property begins with "On", and the property is implemented with a field rather than read/write methods. The value stored by the property is a method pointer, pointing to the event handler procedure. 

In the following example, the TObservedObject class includes an OnPing event, of type TPingEvent. The FOnPing field is used to store the event handler. The event handler in this example, TListener.Ping, prints 'TListener has been pinged!'.

Program EventDemo;
{$APPTYPE CONSOLE}

type
    TPingEvent = procedure of object;
    TObservedObject = class
    private
        FPing: TPingEvent;
    public
        property OnPing: TPingEvent read FPing write FPing;
    end;

    TListener = class
        procedure Ping;
    end;

procedure TListener.Ping;
begin
    writeln('TListener has been pinged.');
end;

var
    observedObject: TObservedObject;
    listener: TListener;

begin
    observedObject := TObservedObject.Create;
    listener := TListener.Create;

    observedObject.OnPing := listener.Ping;

    observedObject.OnPing; // should output 'TListener has been pinged.'

    ReadLn; // pause console before closing
end.

In Delphi for Win32, events can be assigned only a single event handler. If multiple event handlers must be executed in response to an event, the event handler assigned to the event must call any other event handlers. In the following code, a subclass of TListener called TListenerSubclass has its own event handler called Ping2. In this example, the Ping2 event handler must explicitly call the TListener.Ping event handler in order to trigger it in response to the OnPing event.

Program EventDemo2;
{$APPTYPE CONSOLE}

type
    TPingEvent = procedure of object;
    TObservedObject = class
    private
        FPing: TPingEvent;
    public
        property OnPing: TPingEvent read FPing write FPing;
    end;

    TListener = class
        procedure Ping;
  end;

  TListenerSubclass = class (TListener)
    procedure Ping2;
    end;

procedure TListener.Ping;
begin
    writeln('TListener has been pinged.');
end;

procedure TListenerSubclass.Ping2;
begin
  self.Ping;
    writeln('TListenerSubclass has been pinged.');
end;

var
    observedObject: TObservedObject;
    listener: TListenerSubclass;

begin
    observedObject := TObservedObject.Create;
    listener := TListenerSubclass.Create;

    observedObject.OnPing := listener.Ping2;

    observedObject.OnPing; // should output 'TListener has been pinged.' 
      // and then 'TListenerSubclass has been pinged.'

    ReadLn; // pause console before closing
end.

On the .NET platform, Delphi enables multiple event handlers to be applied to the same event. A multicast event is an event that can trigger multiple event handlers. Using mulitcast events can reduce the effort required for maintenance of complex event systems, and improve the readability and flexibility of event handling. 

Multicast events are declared using the add and remove keywords to indicate the field or methods that are used to add or remove event handlers for an event. The Include() and Exclude() standard procedures are used to include and exclude event handlers at runtime. The following code demonstrates the declaration of an event property that uses multicast events.

Program NETEvents;
{$APPTYPE CONSOLE}

type
    TPingEvent = procedure of object;
    TObservedObject = class
    private
        FPing: TPingEvent;
    public
        property OnPing: TPingEvent add FPing remove FPing;
    end;

    TListener = class
        procedure Ping;
  end;

  TListenerSubclass = class (TListener)
    procedure Ping2;
    end;

procedure TListener.Ping;
begin
    writeln('TListener has been pinged.');
end;

procedure TListenerSubclass.Ping2;
begin
    writeln('TListenerSubclass has been pinged.');
end;

var
    observedObject: TObservedObject;
    listener: TListener;
    listenerSubclass: TListenerSubclass;
    testEvent: TPingEvent;

begin
    observedObject := TObservedObject.Create;
    listener := TListener.Create;
    listenerSubclass := TListenerSubclass.Create;

    Include(observedObject.OnPing, listener.Ping);
    Include(observedObject.OnPing, listenerSubclass.Ping2);

    // testEvent := observedObject.OnPing;     // not allowed

    observedObject.FPing(); // should output 'TListener has been pinged.'

    ReadLn; // pause console before closing
end.

The ObservedObject.OnPing property declaration uses the add and remove keywords instead of read and write. The add and remove keywords indicate to the compiler that OnClick is a multicast event. 

A property which represents a multicast event can not be read or written directly; it can only be accessed through the Include() and Exclude() standard procedures. To attempt to read, write or execute a property that has been declared with add and remove is an error at compile time. To execute this event directly, the sample code uses the FPing field rather than the OnPing property.

Copyright(C) 2008 CodeGear(TM). All Rights Reserved.
What do you think about this topic? Send feedback!