The ActionList Component
Delphi’s event architecture is very open: You can write a single event handler and connect it
to the OnClick events of a toolbar button and a menu. You can also connect the same event
handler to different buttons or menu items, as the event handler can use the Sender parameter
to refer to the object that fired the event. It’s a little more difficult to synchronize the status
of toolbar buttons and menu items. If you have a menu item and a toolbar button that both
toggle the same option, every time the option is toggled, you must both add the check mark
to the menu item and change the status of the button to show it pressed.
To overcome this problem, Delphi 4 introduced an event-handling architecture based on
actions. An action (or command) both indicates the operation to do when a menu item or button
is clicked and determines the status of all the elements connected to the action. The connection
of the action with the user interface of the linked controls is very important and
should not be underestimated, because it is where you can get the real advantages of this
NOTE If you have ever written code using the MFC class library of Visual C++, you’ll recognize that a
Delphi action maps to both a command and a CCommandUpdateUI object. The Delphi architecture
is more flexible, though, because it can be extended by subclassing the action classes.
There are many players in this event-handling architecture. The central role is certainly
played by the action objects. An action object has a name, like any other component, and other
properties that will be applied to the linked controls (called action clients). These properties
include the Caption, the graphical representation (ImageIndex), the status (Checked, Enabled,
and Visible), and the user feedback (Hint and HelpContext). There is also the ShortCut and a
list of SecondaryShortCuts, the AutoCheck property for two-state actions, the help support, and
a Category property used to arrange actions in logical groups.
Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com
The base class for an all action object is TBasicAction, which introduces the abstract core
behavior of an action, without any specific binding or correction (not even to menu items or
controls). The derived TContainedAction class introduces properties and methods that enable
actions to appear in an action list or action manager. The further-derived TCustomAction class
introduces support for the properties and methods of menu items and controls that are
linked to action objects. Finally, there is the derived ready-to-use TAction class.
Each action object is connected to one or more client objects through an ActionLink object.
Multiple controls, possibly of different types, can share the same action object, as indicated by
their Action property. Technically, the ActionLink objects maintain a bidirectional connection
between the client object and the action. The ActionLink object is required because the connection
works in both directions. An operation on the object (such as a click) is forwarded to
the action object and results in a call to its OnExecute event; an update to the status of the
action object is reflected in the connected client controls. In other words, one or more client
controls can create an ActionLink, which registers itself with the action object.
You should not set the properties of the client controls you connect with an action, because
the action will override the property values of the client controls. For this reason, you should
generally write the actions first and then create the menu items and buttons you want to connect
with them. Note also that when an action has no OnExecute handler, the client control is
automatically disabled (or grayed), unless the DisableIfNoHandler property is set to False.
The client controls connected to actions are usually menu items and various types of buttons
(push buttons, check boxes, radio buttons, speed buttons, toolbar buttons, and the like),
but nothing prevents you from creating new components that hook into this architecture.
Component writers can even define new actions, as we’ll do in Chapter 11, and new link
Besides a client control, some actions can also have a target component. Some predefined
actions hook to a specific target component (for examples, see the coverage of the DataSet
components in the Chapter 13 section “Looking for Records in a Table”). Other actions
automatically look for a target component in the form that supports the given action, starting
with the active control.
Finally, the action objects are held by an ActionList component, the only class of the basic
architecture that shows up on the Component Palette. The action list receives the execute
actions that aren’t handled by the specific action objects, firing the OnExecuteAction. If even
the action list doesn’t handle the action, Delphi calls the OnExecuteAction event of the
Application object. The ActionList component has a special editor you can use to create
several actions, as you can see in Figure 8.1.
The ActionList Component