Actions came in view in Delphi in version 4.0 and to say the truth I
did not think that this was something really helpful and interesting then.
You know you seldom want to improve something that already works fine and
since my programs worked fine without actions I was not so excited about
them then. I am now!
When CoolDev started the development of CoolMenus Pro I found that
actions made programmer’s life much easier, simplified most of the tasks.
So any time something got to be clicked, checked (or unchecked) I use actions
now. Gosh I am full of actions today. One big action! ;-)
So what is so good about them anyway? The answer is as simple as the
word EVERYTHING. The article you are reading is not intended to be the
complete source of the information. I just will give you few examples and
point out few ways where actions can be of help so you can decide for yourself
if you really want to use all advantages they are ready to give you.
Advantage #1
Suppose you created some utility. You have implemented all the functionality you wanted and the only your concern now is to give the customer an easy way to exit your program. Obviously you have some menu that contains “Close” item which once clicked terminates the program execution. But you decided to add one more option for your customer and placed a button with the same functionality somewhere on your form. Then you added some code and finally you have two routines:
//menu item
procedure TForm1.Close1Click(Sender: TObject);
begin
Close;
end;
//button
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;
Good isn’t it? Now what would it look like with the actions? Add one TActionList component. Now activate its component editor. Either double click on the component or select “Action List Editor” from the component popup menu. Once you are inside the editor add one TAction component. You can:
1. Hit Insert key.
2. Click tool button with appropriate hint.
3. Select appropriate item from the editor’s popup menu.
Now select newly added action and set its Caption property (in Object Inspector) to “Close”. Then add OnExecute event handler:
procedure TForm1.Action1Execute(Sender: TObject);
begin
Close;
end;
All you need to do now is to assign Action properties of our two components
(menu item and button) to the TAction component we have just created. Do
note that once Action is assigned both button and menu item have caption
“Close”.. Also look where OnClick event of the components is pointing to
now.
“So what?” you can say, “Not a big deal. I could live without these
actions at least till 1/1/3000”.. Well on this stage yes. However even
in this small example we now have one routine instead of two and our code
is more readable. I could also tell you that not only our PAS file is smaller
now but our DFM as well. How come? I will explain that later. Anyway imagine
that you use owner drawn menu instead of the normal one and TBitBtn instead
of TButton, and both components have the glyphs. Without actions you would
need to assign THE SAME image to both controls, so you would have to put
the same bitmap into your resources twice. Since bitmaps are sometimes
really big that can be serious economy.
Now one more reason why we have smaller DFM files with the actions.
See how Caption property is declared in TControl:
property Caption: TCaption read GetText write SetText stored IsCaptionStored;
and here is that IsCaptionStored procedure:
function TControl.IsCaptionStored: Boolean;
begin
Result := (ActionLink = nil) or not ActionLink.IsCaptionLinked;
end;
In other words caption of our button control (and the same with menu item) is stored in the DFM only if there is no Action assigned. If there is an Action then we save Caption assigned to TAction only. Now all the controls that are linked to this Action (no matter how many of them you have) will use the same caption saved only once. Keep in mind that TAction has another properties such as:
Caption,
Checked,
Enabled,
HelpContext,
Hint,
ImageIndex,
ShortCut,
Visible
apart from Caption and that with TActionList we can use just one TImageList for all the controls... Well, this is quite an economy.
Advantage #2
Now for some more. Suppose you have another form that displays some info for your users and you want this info to be available for registered users only. You perform some checking and if you found out that this copy of the program is used by a registered user you enable appropriate button and menu item, otherwise you disable them:
begin
InfoMenuItem.Enabled := ISRegisteredUser;
InfoButton.Enabled := ISRegisteredUser;
end;
With Actions the above code looks as follows:
begin
Action1.Enabled := ISRegisteredUser;
end;
This time the advantage is not the fact that you produce more compact code, the real kick is that you are modifying state of two controls in one line without calling any methods or modifying any properties of these controls directly! When you update Enabled state of the Action all involved controls and components are automatically updated accordingly.
Advantage #3
How about standard actions? You would like to create “Window” menu and just don’t know what items should appear there and how to implement these “Tile”, “Cascade” and other commands? Really how? No problem, all you have to do is to create TActionList component, activate its component editor and pick up “Create Standard Action” command from the toolbar (or from the popup menu) there. You will see the list of items then where you will have to select those related to category “Window” and add all or just necessary standard Window commands. The good news #1 - once you created and assigned menu items to these actions you do not need to write any code for OnExecute. Everything has already been written. Good news #2 - if you created and assigned TImageList, it will contain all necessary images for the Actions that you have just added.
Advantage #4
Now the last but not the least. You can plan and create well-organized
projects with the Actions. For example create TCustomModule and there TActionList
and that will be the heart of your application. All events and code will
be placed there. Then you add the name of this unit to the “uses” section
of all other units (you might want to do it in implementation section to
avoid circular reference problem) and you can assign Action property of
your visual controls to the Actions created in your custom module.
Every TAction descendant has the Category property. It is not going
to be used in Run-Time but you may use it in Design-Time to organize your
actions and make editor much easier to navigate and manipulate.
The only thing you should be aware of with the actions is the fact that
components' published properties (those listed above) are not saved if
there is an action assigned (I already explained why). Thus there is no
reason to modify them. Plus if you use Actions it is a bad style to access
properties of the control directly. You can never tell (actually you can
but… ;-) when they are overridden by the values that Actions' properties
have.
One more limitation is that any standard action can only be used with
those controls' events that have exactly one parameter - (Sender: TObject).
That means you can not create Action for OnMouseMove or OnKeyDown event
of a form (but still can for OnActivate or OnClose).
Ok that’s about all I can say in regard to these pretty Actions and
what gain they can make for you and your applications. One more important
thing about them is of course that you can create and register your own
Action types with the required functionality and parameters list. But as
they say “that’s for another story”.
Artem A. Berman
[email protected] <mailto:[email protected]>
<http://www.cooldev.com/>