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

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

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

I am trying to get this blog listed on DelphiFeeds.com
If you like this post, please +1 for me here on Delphi Feeds

and here on BeginEnd

Thank You !

About The Author

scott_circle
The Usual Suspect
– Scott Hollows –

  • Oracle and Delphi software developer.
  • Australian Delphi User Group – Western Australia Chief Cat Herder
  • Australian Delphi User Group – President
blog email linkedinlogo

Author: Scott Hollows

Enterprise software developer based in Perth, Western Australia. Focused on Oracle, Delphi, Data Warehouse design and ETL, Data Architect, Business Intelligence, Oracle performance tuning. President of the Australian Delphi User Group (ADUG) LinkedIn www.linkedin.com/in/scotthollows

2 thoughts on “Change Delphi control values without change events firing (VCL and FMX)”

  1. hi.
    some improvement:

    procedure SetEditValue(aField: TEdit; aValue: string);
    var
    vEventHandler: TNotifyEvent;
    begin
    if aField.OnChange = nil then
    aField.Text:= aValue
    else
    begin
    vEventHandler := aField.OnChange;
    aField.OnChange := nil;
    try
    aField.Text:= aValue;
    finally
    aField.OnChange := vEventHandler;
    end;
    end;
    end;

  2. Good points Mamuka

    I like my original code as a starting point as its easier to understand so Im going to leave it there.

    Ive added a more robust version below the first, similar to Mamuka’s proposal, with one minor change.
    IF ONCHANGE = NIL fails to compile so another technique has to be used to check if the event is assigned.
    So instead, we have to use ASSIGNED to do the comparison, like this …

    IF NOT ASSIGNED (aField.OnChange) THEN

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s