In Part 4, we saw how TEdit and TMemo's ancestor: TCustomEdit uses observers to manage passive states like ReadOnly. However, TCustomCheckBox and hence, any descendants, demonstrates a much more aggressive use of the pattern: Active Interception.
ReadOnly
A TCustomCheckBox doesn't just wait for a value to change; it actively consults its observers during user input. In the KeyPress override, the TCustomCheckBox checks if an EditLinkID observer is present and if is currently read-only.
KeyPress
EditLinkID
procedure TCustomCheckBox.KeyPress(var Key: Char); begin if Observers.IsObserving(TObserverMapping.EditLinkID) then begin if TLinkObservers.EditLinkIsReadOnly(Observers) then Exit; // Block input entirely if the observer says so case Key of #8, ' ': // Backspace or Space if TLinkObservers.EditLinkEdit(Observers) then TLinkObservers.EditLinkModified(Observers); end; end; inherited KeyPress(Key); // ... post-inheritance logic end;
The TCustomCheckBox also uses ObserverToggle to modify its own internal behavior based on data requirements. For instance, it can dynamically enable or disable the cbGrayed state based on whether the data field is "Required."
ObserverToggle
cbGrayed
procedure TCustomCheckBox.ObserverToggle(const AObserver: IObserver; const Value: Boolean); var LEditLinkObserver: IEditLinkObserver; begin if Value and Supports(AObserver, IEditLinkObserver, LEditLinkObserver) then AllowGrayed := not LEditLinkObserver.IsRequired else AllowGrayed := False; end;
The most critical aspect of the TCustomCheckBox implementation is its use of try...except blocks during the Toggle method. If an observer fails to update the underlying data (perhaps due to a database validation error), the TCustomCheckBox ensures the UI stays in sync by catching the exception and re-focusing the control.
try...except
Toggle
try if Observers.IsObserving(TObserverMapping.ControlValueID) then begin TLinkObservers.ControlValueModified(Observers); TLinkObservers.ControlValueTrackUpdate(Observers); end; except SetFocus; raise; // Re-raise to let the application handle the error end;
This reveals the VCL Observer pattern as a sophisticated "gatekeeper" for data integrity. It isn't just about moving data; it's about validating the intent to change data before the UI reflects that change.
In Part 6: We move from VCL internals to our own code, implementing a reusable TBaseVclObserver framework to simplify observer management.
TBaseVclObserver
In 2017, with the release of Delphi 10.2 Tokyo, Embarcadero introduced a specialized implementation of the Observer pattern into the System.Classes unit. While it has been in the wild for 9 years, it remains a "hidden" architecture for many, primarily because it serves as the invisible engine behind LiveBindings. Other than live bindings, you can also use the Observer pattern as a way to update component settings to the Windows registry, an .ini file, or persist it elsewhere.
System.Classes