Change Delphi control values without change events firing (VCL and FMX) by Scott Hollows

emailx45

Местный
Регистрация
5 Май 2008
Сообщения
3,571
Реакции
2,438
Credits
573
Change Delphi control values without change events firing (VCL and FMX)
Scott Hollows - 14/Sep/2016
[SHOWTOGROUPS=4,20]
Change a Delphi field value without the change events firing (OnChange)

Sometimes you want to change the value of a Delphi control without its change event firing.

For example, when your application starts up and you are initializing control values. The rest of the application isn’t setup yet so you might not want the control change event firing and executing other code.

Ill show you how to do it with just one line of code using my custom unit for VCL and Firemonkey
changeeventsdisable1


Ill use the TEdit as an example. It has an OnChange event that updates the other control (DescriptionEdit) in this example.

The OnChange event is actually a property that we can change at runtime just like many other properties.

The trick here is to disable the event by setting OnChange to nil at runtime. We also need to save and restore the original event. Like this ..
Код:
var
vEventHandler : TNotifyEvent;
begin
vEventHandler := Edit1.OnChange; // save the change event
Edit1.OnChange := nil; // clear it so it wont fire
Edit1.text := 'Hello World'; // change the value
Edit1.OnChange := vEventHandler; // restore the event
end;

Code Reuse
Lets move the code into a procedure so we can reuse it
Код:
procedure SetEditValue (aField : TEdit; aValue : string);
// change the controls value without firing change events
var
vEventHandler : TNotifyEvent;
begin
vEventHandler := aField.OnChange; // save the event
aField.OnChange := nil; // clear it so it wont fire
aField.text := aValue; // change field value
aField.OnChange := vEventHandler; // restore the event
end;

Now we can change the field value with just one line of code, like this:
Код:
SetEditValue (Edit1,'This Is The New Value');

Advanced – Making it more robust
A reader (Mamuka Glonti) pointed out a few possible improvements, so lets look at those now
This probably wont make a notable different in performance but it will appeal to the code perfectionists. You can use the more basic version above that is easier to understand or the more advanced version that is more robust and has slighter better performance (I measured it as 3 itty-bitty-mini seconds) – the choice is yours.
  1. Added error handling in case assigning a value to the control results in an error
  2. Dont both with all this if the OnChange event has not been assigned in the first place
Код:
procedure SetEditValue (aField : TEdit; aValue : string);
// change the controls value without firing change events
var
vEventHandler : TNotifyEvent;
begin
if not assigned(aField.OnChange) then // if event is not in use
aField.text := aValue // just change the field value
else
begin
vEventHandler := aField.OnChange; // save the event
aField.OnChange := nil; // clear it so it wont fire
try
aField.text := aValue; // change field value
finally
aField.OnChange := vEventHandler; // restore the event
end;
end;
end;

Using Assigned for event comparison
You have to use NOT ASSIGNED to check if the event is in use or not.

You cant use IF AFIELD.ONCHANGE = NIL because that will fail to compile.

There is probably a good and logical reason for that, but I dont care, I just know it fails to compile and you have to use another way to check if OnChanged is in use.
NOT ASSIGNED does the job perfectly

My utility unit
With this all of this in mind, I created a utility unit with support for a different types of controls (TEdits, TCheckbox, TSpinBox). You can easily add support for other types of controls based on these

FMX vs VCL
I started by creating the VCL unit and then copied and modified it for FMX.

Some minor changes were needed to FMX-ize the unit but it was fairly easy to convert it from VCL to FMX
  1. For Checkboxes OnClick fired in VCL but OnChecked fired in FMX
    I decided to nil out both OnChecked and OnClick in FMX to play it safe in case that changes in future
  2. Different units in the USES clauses
Ideas for Improvement
This post was focused on explaining the basics but you can take it further if you want. Here are some ideas
  1. Have a single SetControlValue procedure that supports many control types instead of just one control per procedure.
  2. Make one unit support both VCL and FMX.
  3. Use Delphi RTTL to support a larger number of controls without coding specifically for each control or including the unit for the control
Download Source
Для просмотра ссылки Войди или Зарегистрируйся full source here.

I have included sample projects for VCL and Firemonkey for Delphi 10.1 Berlin. The code should work in earlier versions of Delphi with minimal changes.
I have included support for a few different types of controls (TEdits, TCheckbox, TSpinBox) and you can easily add support for other controls.

[/SHOWTOGROUPS]