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
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;
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.
- Added error handling in case assigning a value to the control results in an error
- 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
- 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
- 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
- Have a single SetControlValue procedure that supports many control types instead of just one control per procedure.
- Make one unit support both VCL and FMX.
- Use Delphi RTTL to support a larger number of controls without coding specifically for each control or including the unit for the control
Download 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.
+1 this post
and here on BeginEnd
Thank You !
About The Author
The Usual Suspect
– Scott Hollows –
- Oracle and Delphi software developer.
- Australian Delphi User Group – Western Australia Chief Cat Herder
- Australian Delphi User Group – President