Understanding how the VCL uses observers internally is fascinating, but to use this pattern in our own applications, we need a clean way to implement these interfaces. Without a base framework, you would find yourself writing the same boilerplate code for GetActive, SetActive, and interface management over and over.
GetActive
SetActive
First, we need a class that implements the basic IObserver interface. This class handles the Active state and the OnObserverToggle event, which we've seen is crucial for UI synchronization.
Active
OnObserverToggle
TBaseObserver = class(TInterfacedObject, IObserver) protected FActive: LongBool; FOnObserverToggle: TObserverToggleEvent; public constructor Create; procedure Removed; virtual; function GetActive: Boolean; virtual; procedure SetActive(Value: Boolean); virtual; property Active: Boolean read GetActive write SetActive; // ... Getters/Setters for OnObserverToggle end;
Since we are primarily working with UI controls, our framework needs to know which TWinControl we are observing. By creating a TBaseVclObserver, we can automate the process of adding ourselves to the component's observer list.
TBaseVclObserver
TBaseVclObserver = class(TBaseObserver) protected FControl: TWinControl; procedure AddControlValueObserver(const AControl: TWinControl); virtual; procedure AddEditLinkObserver(const AControl: TWinControl); virtual; procedure AddAllObservers(const AControl: TWinControl); virtual; public constructor Create(const AControl: TWinControl); virtual; end;
The beauty of this framework is the AddAllObservers method. Instead of manually calling AddObserver for every ID, the base class can iterate through the standard IDs and register the observer where supported. This utilizes the Observers property we explored in Part 2.
AddAllObservers
AddObserver
Observers
constructor TBaseVclObserver.Create(const AControl: TWinControl); begin inherited Create; FControl := AControl; if FControl <> nil then AddAllObservers(FControl); end; procedure TBaseVclObserver.AddControlValueObserver(const AControl: TWinControl); begin AControl.Observers.AddObserver(TObserverMapping.ControlValueID, Self); end;
By moving this logic into a base framework, we've turned a complex interface implementation into a simple inheritance task. Now, creating an observer for a TMemo or a custom control is as easy as overriding a few specific data-handling methods.
TMemo
Learn the command line used to compile System.pas in Delphi
A method to design records so that they're allocated on a specific byte boundary, such as 16 bytes, 512 bytes, 4096 bytes, etc.
Learn why the map is cool in Go!