In the previous articles, we established how the observer engine is initialized. Now, let’s look at how a professional-grade component like TEdit uses these hooks to manage UI state. We'll look at its ancestor: TCustomEdit.
For a TCustomEdit to participate in the observer pattern, it must explicitly signal support for specific IDs in CanObserve. Specifically, it looks for EditLinkID (for data binding) and ControlValueID (for simple change monitoring).
TCustomEdit
CanObserve
EditLinkID
ControlValueID
function TCustomEdit.CanObserve(const ID: Integer): Boolean; begin Result := False; if ID = TObserverMapping.EditLinkID then Result := True else if ID = TObserverMapping.ControlValueID then Result := True; end;
The most sophisticated part of the TCustomEdit implementation is how it handles property overrides. When an observer is added, the component hooks into the OnObserverToggle event. This allows the component to save its current design-time state before the observer potentially modifies it.
OnObserverToggle
procedure TCustomEdit.ObserverAdded(const ID: Integer; const Observer: IObserver); begin if ID = TObserverMapping.EditLinkID then Observer.OnObserverToggle := ObserverToggle; end;
When an observer becomes active (Value = True), the edit control saves its current ReadOnly, Alignment, and MaxLength properties into "Saved" fields. It then applies the constraints defined by the observer.
Value = True
ReadOnly
Alignment
MaxLength
procedure TCustomEdit.ObserverToggle(const AObserver: IObserver; const Value: Boolean); var LEditLinkObserver: IEditLinkObserver; LEditFormatLink: IEditFormatLink; begin if Supports(AObserver, IEditLinkObserver, LEditLinkObserver) then begin if Value then // Observer is Activating begin FSavedReadOnly := ReadOnly; FSavedAlignment := Alignment; FSavedMaxLength := MaxLength; if LEditLinkObserver.IsReadOnly then ReadOnly := True; if Supports(LEditLinkObserver.FormatLink, IEditFormatLink, LEditFormatLink) then begin Alignment := LEditFormatLink.Alignment; MaxLength := LEditFormatLink.MaxLength; end; end else // Observer is Deactivating begin if not (csDestroying in ComponentState) then begin ReadOnly := FSavedReadOnly; Alignment := FSavedAlignment; MaxLength := FSavedMaxLength; end; end; end; end;
This "Save/Restore" pattern is vital. It ensures that if the observer is ever disconnected or disabled, the UI control gracefully reverts to the properties the developer originally set in the Object Inspector.
This level of integration ensures that the UI remains consistent with the data source. If the underlying data field is ReadOnly, TEdit's ReadOnly property is toggled to True, preventing user input at the Windows API level. It isn't just a visual trick; it's a deep structural sync.
In Part 5: We see how TCustomCheckBox takes this a step further by intercepting user input in real-time to enforce business rules.
TCustomCheckBox
A method pointer is now the same as a global procedure, ie, procedure of object = procedure.