Handling the OnChange event handler on a TEdit is great until you programmatically change the text and trigger your OnChange event handler again. It gets really fun if you have two TEdit boxes that update each other in their OnChange event handler. I was reading Automate Restorable Operations with Custom Managed Records by Erik van Bilsen over on Griggy’s Blog and I had a great idea 💡 to automate the disabling and re-enabling of the OnChange event handler!
Custom Managed Records (or CMR as Eric calls them) is a super powerful language feature introduced in 10.4 Sydney. It takes the Record data type and adds Initialization and Finalization methods that are automatically called (as well as other features). Since Records are reference counted Finalization method is automatically called when the last reference goes out of scope. Additionally, it uses try..finally blocks to make sure the finalization method is called.
So instead of writing code like…
procedure TForm13.Edit1Change(Sender: TObject);
begin
var savedEvent := Edit2.OnChange;
Edit2.OnChange := nil;
try
Edit2.Text := Rot47(Edit1.Text);
finally
edit2.OnChange := savedEvent;
end;
end;
It can be dramatically simplified to the following, while still having the same effect…
procedure TForm13.Edit2Change(Sender: TObject);
begin
var Ignorer := TIgnoreEditChanges.Create(Edit1);
Edit1.Text := Rot47(Edit2.Text);
end;
It is important that you assign the instance created, otherwise it goes out of scope immediately, and you loose the benefit.
Originally I implemented this to only support TCustomEdit, but then I got to thinking about TMemo and a number of the other components with OnChange that don’t share a common ancestor. Then I realized I’d probably be better off using RTTI (which should make it agnostic to FMX vs VCL) and maybe a fluent syntax with generics . . . so this isn’t fully implemented yet. . . . Use it at your own risk.
Random tip #1 for handling OnChange events in FMX/FireMonkey forms – if you want to be notified immediately of changes, and not only when the user exits the field, you can handle the OnTyping event, or map the two together.
Random tip #2 I’ve posted to GitHub Gist my Rot47 code and the IgnoreEditChanges unit.
unit IgnoreEditChanges;
interface
uses
System.Classes;
type
TIgnoreEditChanges = record
private
FEdit: TObject;
FOnChange: TNotifyEvent;
public
constructor Create(const AnEdit: TObject);
class operator Initialize(out ADest: TIgnoreEditChanges);
class operator Finalize(var ADest: TIgnoreEditChanges);
class operator Assign(var ADest: TIgnoreEditChanges;
const [ref] ASrc: TIgnoreEditChanges);
end;
implementation
uses
FMX.Controls, FMX.Edit, FMX.DateTimeCtrls, FMX.ComboEdit, FMX.Memo;
constructor TIgnoreEditChanges.Create(const AnEdit: TObject);
begin
Assert(Assigned(AnEdit));
FEdit := AnEdit;
if FEdit is TCustomEdit then
begin
FOnChange := TCustomEdit(AnEdit).OnChange;
TCustomEdit(AnEdit).OnChange := nil;
end
else if FEdit is TCustomDateTimeEdit then
begin
FOnChange := TCustomDateTimeEdit(AnEdit).OnChange;
TCustomDateTimeEdit(AnEdit).OnChange := nil;
end
else if FEdit is TCustomMemo then
begin
FOnChange := TCustomDateTimeEdit(AnEdit).OnChange;
TCustomMemo(AnEdit).OnChange := nil;
end
else
raise EInvalidOperation.Create(
'TIgnoreEditChanges does not support objects of type ' + AnEdit.ClassName);
end;
class operator TIgnoreEditChanges.Initialize(out ADest: TIgnoreEditChanges);
begin
ADest.FEdit := nil;
end;
class operator TIgnoreEditChanges.Finalize(var ADest: TIgnoreEditChanges);
begin
if Assigned(ADest.FEdit) then
begin
if ADest.FEdit is TCustomEdit then
TCustomEdit(ADest.FEdit).OnChange := ADest.FOnChange
else if ADest.FEdit is TCustomDateTimeEdit then
TCustomDateTimeEdit(ADest.FEdit).OnChange := ADest.FOnChange
else if ADest.FEdit is TCustomMemo then
TCustomMemo(ADest.FEdit).OnChange := ADest.FOnChange
end;
end;
class operator TIgnoreEditChanges.Assign(var ADest: TIgnoreEditChanges;
const [ref] ASrc: TIgnoreEditChanges);
begin
raise EInvalidOperation.Create(
'TIgnoreEditChange records cannot be copied.')
end;
end.
What cool uses are you finding for Custom Managed Records?
Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Free Delphi Community Edition Free C++Builder Community Edition








