Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use an action to determine a control's visibility?

Tags:

delphi

I'm trying to use an action to control the visibility of a control. My code looks like this:

Pascal file

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ActnList, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ActionList1: TActionList;
    Action1: TAction;
    CheckBox1: TCheckBox;
    procedure Action1Update(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Action1Update(Sender: TObject);
begin
  (Sender as TAction).Visible := CheckBox1.Checked;
end;

end.

Form file

object Form1: TForm1
  object Button1: TButton
    Left = 8
    Top = 31
    Action = Action1
  end
  object CheckBox1: TCheckBox
    Left = 8
    Top = 8
    Caption = 'CheckBox1'
    Checked = True
    State = cbChecked
  end
  object ActionList1: TActionList
    Left = 128
    Top = 8
    object Action1: TAction
      Caption = 'Action1'
      OnUpdate = Action1Update
    end
  end
end

When the form first runs the button is visible and the check box is checked. Then I un-check the check box and the button disappears. When I re-check the check box the button fails to re-appear.

I think the reason for this can be found inside the following local function in TCustomForm.UpdateActions:

procedure TraverseClients(Container: TWinControl);
var
  I: Integer;
  Control: TControl;
begin
  if Container.Showing and not (csDesigning in Container.ComponentState) then
    for I := 0 to Container.ControlCount - 1 do
    begin
      Control := Container.Controls[I];
      if (csActionClient in Control.ControlStyle) and Control.Visible then
          Control.InitiateAction;
      if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
        TraverseClients(TWinControl(Control));
    end;
end;

The check of Control.Visible appears to block my action ever getting a chance to update itself again.

Have I diagnosed the issue correctly? Is this by design or should I submit a QC report? Does anyone know of a workaround?

like image 609
David Heffernan Avatar asked Apr 12 '12 16:04

David Heffernan


3 Answers

Your diagnosis is correct. Actions have worked that way since they were first introduced to Delphi.

I expect it's by design (an optimization to avoid excessive updating of the text and other visual aspects of invisible controls, probably), but that doesn't mean the design is good.

Any workaround I can imagine will involve your checkbox code directly manipulating the affected actions, which isn't very elegant since it shouldn't have to be aware of everything else it might affect — that's what the OnUpdate event is supposed to do for you. When the checkbox gets checked, call Action1.Update, or if that doesn't work, then Action1.Visible := True.

You could also put your action-updating code in the TActionList.OnUpdate event instead.

like image 54
Rob Kennedy Avatar answered Oct 09 '22 09:10

Rob Kennedy


Yes your diagnosis is correct as Rob already said and explained. A work around is not to use individual TAction's OnUpdate event handlers, but to use the TActionList's OnUpdate event handler.

procedure TForm1.ActionList1Update(Action: TBasicAction; var Handled: Boolean);
begin
  Handled := True;
  Action1.Visible := CheckBox1.Checked;
end;

You do need at least one visible control with a linked action for this to work, otherwise the ActionList's OnUpdate handler won't be called either.

Btw, when Actions and ActionLists were first introduced, Ray Konopka (Raize components) wrote a couple of articles on their implementation and gave very sound advice on how to use them. Ever since, I have adopted the practice of to use the OnExecute of each individual Action, but to use the OnUpdate of the ActionList. Also, the first thing I do in that handler is to set Handled to True so it won't be called more than necessary, and to only ever change an Action's Enabled property once in that handler so the GUI won't flicker as a result of turning it off and then on.

Article by Ray Konopka is "Effectively using Action Lists" : http://edn.embarcadero.com/article/27058 Ray used to have three articles on his own site, but on embarcadero there is but one, but it may well be the "combined" version (don't have the originals handy).

like image 21
Marjan Venema Avatar answered Oct 09 '22 09:10

Marjan Venema


ActionUpdate event is not called when related controls are not visible. Try to explicity call ActionUpdate on Checkbox1's click event.

like image 21
Christopher Ramírez Avatar answered Oct 09 '22 10:10

Christopher Ramírez