Delphi. How to Disable/Enable controls without triggering controls events
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.
Comments
-
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 callDataSet.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 almost 12 yearsThis 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 almost 12 yearsHmm 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 almost 12 yearsNo, 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 almost 12 yearsI'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 almost 12 yearsComponentCount 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 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 almost 12 yearsit 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 almost 12 yearsinstead 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.