Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does TAction.OnUpdate event degrade the performance?

In Delphi XE7, I use this trick to automatically enable or disable a toolbar button ("Edit ListView Item") according to whether an item in the ListView is selected or not, to prevent the user to click on the button if there is no ListView Item selected:

  • Put a TActionList on a VCL Form.
  • In the ActionList create an action actTest.
  • Put a TButton on the form.
  • Assign the action actTest to the button.
  • Put a TListView on the form.
  • In the ListView create two items.
  • In the OnUpdate event of the actTest action write:

     procedure TForm1.actTestUpdate(Sender: TObject);
     begin
       actTest.Enabled := ListView1.SelCount > 0;
       CodeSite.Send('actTestUpdate'); // gets fired very often!
     end;
    

Now you can see that the button becomes enabled or disabled according to whether an item in the ListView is selected or not, independently from whether you select/deselect items with the mouse or with the keyboard or programmatically.

However, in the CodeSite Live Viewer I can see that the actTestUpdate event is fired continuously and very often, so the statement actTest.Enabled := ListView1.SelCount > 0; gets executed VERY OFTEN.

So my question is: Does this degrade the performance? If yes, is there another trick to achieve the above purpose?

like image 300
user1580348 Avatar asked Mar 01 '15 10:03

user1580348


2 Answers

Generally

Yes, an OnUpdate event handler takes time, just as any other routine does. Multiple handlers take a multiple of that time. The gross of all that code will evaluate conditions resulting in just nothing to do. In that sense, you could conclude that this update mechanism degrades performance. Especially considering these update events occur quite often:

Occurs when the application is idle or when the action list updates.

That could be a reason to spare its use. But you should realize that the evaluation of a single expression mostly does not take that much time. Also, realize that regardless of action updates, your application performs (much more heavy) calculations and operations on every single mouse move.

When you keep the code duration in action update events to a minimal, e.g. no password checking via a database connection, then performance will appear just normal. If you háve lengthy operations linked to updating actions, then fall back on manual updates in those specific situations.

Note that performance can be slightly gained by not using the individual OnUpdate events of the Actions, but the OnUpdate event of the ActionList instead which has a Handled parameter to cancel further processing, with the additional benefit of centralization and categorization.

Specifically

Your ListView1.SelCount sends a WinAPI message to the control to retrieve the selection count. That is a tiny operation and I would not bother its time needed.

An alternative is to update the action in the ListView's OnSelectItem event. That event will catch all selection changes due to mouse and keyboard interaction, as well as setting the individual items' Selected property:

procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
begin
  actTest.Enabled := ListView1.SelCount > 0;
end;

However, the ListView nor the VCL do not provide anything to signal only between SelCount = 0 and SelCount > 0, so you will evaluate that line of code more then strictly necessary anyway.

Assuming MultiSelect is true, you could also count the selection changes yourself to eliminate the need for calling SelCount:

  private
    FListViewSelected: Longbool;

procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
begin
  if Selected then
    Inc(FListViewSelected)
  else
    Dec(FListViewSelected);
  actTest.Enabled := FListViewSelected;
end;

Or test for the selected item being nil:

procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
begin
  actTest.Enabled := ListView1.Selected <> nil;
end;

But then again, there really is no reason left for not using the OnUpdate event:

procedure TForm1.ActionList1Update(Action: TBasicAction; var Handled: Boolean);
begin
  actTest.Enabled := ListView1.Selected <> nil;
  Handled := True;
end;
like image 135
NGLN Avatar answered Nov 05 '22 03:11

NGLN


If you have (or plan to have) many actions you might want to set Application.ActionUpdateDelay to e.g. 50 milliseconds. This can improve the performance noticeable.

Also, again if you have many actions, I would suggest you try to use TForm.UpdateActions instead of defining TAction.OnUpdate for each action. It will make the code more readable.

like image 9
pyxidata Avatar answered Nov 05 '22 01:11

pyxidata