Delphi. How to Disable/Enable controls without triggering controls events

12,589

Solution 1

Building on Guillem's post:

Turn off everything:

Traverse each component on the form with the for-loop, shown below, changing the properties to the desired value.

If you want to later revert back to the original property values, then you must save the original value (as OldEvent is used below.)

Edit: The code below shows the key concept being discussed. If components are being added or deleted at run-time, or if you'd like to use the absolutely least amount of memory, then use a dynamic array, and as Pieter suggests, store pointers to the components rather than indexing to them.

  const
    MAX_COMPONENTS_ON_PAGE = 100; // arbitrarily larger than what you'd expect. (Use a dynamic array if this worries you.
  var 
     OldEvent: Array[0.. MAX_COMPONENTS_ON_PAGE - 1] of TNotifyEvent; // save original values here
     i: Integer;
  begin                                 
   for i := 0 to ComponentCount - 1 do 
   begin
     if (Components[i] is TCheckBox)  then 
        begin
          OldEvent[i] := TCheckBox(Components[i]).OnClick; // remember old state  
          TCheckBox(Components[i]).OnClick := nil;
        end
      else if (Components[i] is TEdit) then
         begin
           OldEvent[i] := TEdit(Components[i]).OnClick; // remember old state  
           TEdit(Components[i]).OnClick := nil;          
         end;
    end;

Revert to former values

 for i := 0 to ComponentCount - 1 do 
 begin
   if (Components[i] is TCheckBox)  then
      TCheckBox(Components[i]).OnClick := OldEvent[i]
   else if (Components[i] is TEdit)  then
      TEdit(Components[i]).OnClick := OldEvent[i];
 end;

There may be a way to fold all of the if-statements into one generic test that answers "Does this component have an OnClickEvent" -- but I don't know what it is.

Hopefully someone will constructively criticize my answer (rather than just down voting it.) But, hopefully what I've shown above will be workable.

Solution 2

One way to do this is following:

var
  Event : TNotifyEvent;    
begin
  Event := myCheckbox.OnClick;
  try
    myCheckbox.OnClick := nil;
    //your code here
  finally
    myCheckbox.OnClick := Event;
  end;
end;

HTH

Solution 3

The internal design of the TCustomCheckBox is that it triggers the Click method every time the Checked property if changed. Be it by actually clicking it or setting it in code. And this is happening here when you call EnableControls because the control gets updated to display the value of the linked field in your dataset.

TButtonControl (which is what TCustomCheckBox inherits from) has the property ClicksDisabled. Use this instead of (or in addition to) the DisableControls/EnableControls call. Unfortunately it is protected and not made public by TCustomCheckBox but you can use a small hack to access it:

type
  TButtonControlAccess = class(TButtonControl)
  public
    property ClicksDisabled;
  end;

...

TButtonControlAccess(MyCheckBox1).ClicksDisabled := True;
// do some dataset stuff
TButtonControlAccess(MyCheckBox1).ClicksDisabled := False;

Of course you can put this into a method that checks all components and sets this property if the control inherits from TCustomCheckBox or some other criteria.

Share:
12,589
ertx
Author by

ertx

$C00000FD

Updated on June 05, 2022

Comments

  • ertx
    ertx almost 2 years

    I have a DataSet (TZQuery), which has several boolean fields, that have TDBCheckBoxes assigned to them.

    These CheckBoxes have "OnClick" events assigned to them and they are triggered whenever I change field values (which are assigned to checkboxes).

    The problem is that I do not need these events triggerred, during many operations i do with the dataset.

    I've tried calling DataSet.DisableControls, but then events are called right after i call DataSet.EnableControls.

    So my question is - is there a way to disable triggering Data-aware controls events.

    Edit (bigger picture):

    If an exception happens while let's say saving data, i have to load the default values (or the values i've had before saving it). Now while loading that data, all these events (TDBCheckBoxes and other data-aware controls) are triggered, which do all sorts of operations which create lag and sometimes even unwanted changes of data, i'm looking for an universal solution of disabling them all for a short period of time.

  • ertx
    ertx almost 12 years
    This will not work since the code is templated and i do not know how many checkboxes might be assigned to a field, or which checkboxes are assigned to which field
  • Guillem Vicens
    Guillem Vicens almost 12 years
    Hmm ok, can you please ellaborate on how your application assigns the checkboxes to the fields? From your comment I understand these checkboxes are used in a grid. Am I guessing right?
  • ertx
    ertx almost 12 years
    No, I have an inherited form, in which, checkboxes are assigned manualy, in design-time, but checkbox is only an example i'm having same problem with TEdits etc.. I'll try to edit original post to give you a better view on the problem.
  • ertx
    ertx almost 12 years
    I've concidered using such code but i'm worried ComponentCount may change, or somehow mess up order of components in the form while actions are being taken
  • RobertFrank
    RobertFrank almost 12 years
    ComponentCount won't change and the order of the components won't change unless you're dynamically adding and removing components at runtime, AFAIK. @ertx: thanks for your necessary edit to the code.
  • RobertFrank
    RobertFrank almost 12 years
    @ertx: On second thought, I don't know why you added the Index variable. Using the for-loop variable "i" as a subscript, AFAIK, would work fine.
  • ertx
    ertx almost 12 years
    it would skip the indexes and record only those array elements, which represent TEdits and TCheckBoxes, resulting in an empty array elements (not sure if that would allocate more memory or not), either way it was an [0] hardcoded before, and i'll accept your answer, thank's for the effort
  • Pieter B
    Pieter B almost 12 years
    instead of taking the index, make a list of the pointers to the components you're disabling. Then when you are re-enabling them you can go through the list of pointers. This makes you indepent of whether between the dis- and enabling the componentcount changes. Just make sure to catch the exception, because a checkbox might have gone.