Have you ever needed for a specific Delphi control, like a TButton, to have just one more property or a method that is a “must have” for your current application?
Most Delphi developers, when they need a TMySuperButton, would either look for a third-party VCL solution or would try creating a derived control.
What if you do not want to have this MySuperButton permanently in the Component/Tool Palette – since it solves one problem only for this particular application you are developing?
What’s more … how about having a TButton with more properties and methods, some application specific extra behavior, and not TMySupperButton?
How about extending what TButton has without the need to create a derived class with a different name?
You could think now of class helpers, but class helpers would not allow extending the class by new virtual methods nor instance fields.
What most beginners do not know is that they *can* create their own custom controls derived from the existing VCL set by creating a, so called, interceptor class (aka interposer class) that has the same name as the class being extended (“interceptor” being my preferred naming).
Delphi Interceptor/Interposer Classes
First, having two classes with the same name in Delphi is possible if those two classes are in separate units.
Next, the following declaration is legal:
type TButton = class(StdCtrls.TButton)
The above code creates an interceptor class for a TButton control by extending the “original” TButton declared in the VCL’s StdCtrls unit.
Finally, the above declaration needs to be placed in a separate unit.
Most important, the unit hosting the interceptor class needs to be listed in the uses clause *after* the unit defining the base class.
TButton = class(TButton) – an Interceptor Class Example
Here’s one interceptor class for the TButton.
A public property “LastClickTime” is added. The original TButton’s Click procedure is overridden – Click raises the OnClick event.
Using the overridden Click procedure our TButton interceptor will not allow running the OnClick event handler if the last click on the button happened during the last two second, only a Beep sound will play.
Ok, a dummy example, but serves the purpose.
unit interceptors; interface uses stdctrls, sysutils, DateUtils; type TButton = class(stdctrls.TButton) private fLastClickTime: TDateTime; public procedure Click; override; public property LastClickTime : TDateTime read fLastClickTime write fLastClickTime; end; implementation procedure TButton.Click; const ClickWaitPeriod = 2; //seconds var clickTime : TDateTime; begin clickTime := Now; if SecondsBetween(clickTime, LastClickTIme) > ClickWaitPeriod then inherited else Beep; LastClickTime := clickTime; end; end.
Now, drop a standard button (TButton) on a Delphi form. Attach some event handler to its OnClick event.
Most importantly, add the “interceptors” unit to the uses list as the last unit listed, or at least after the “stdctrls” unit!
Try clicking the button – note that the code executes only if you wait for two second after the last click (or a beep sounds).
That’s it. Using the above you can easily extend Delphi classes by adding extra properties and methods – without the need to create derived classes and have them in the component palette.
Ah almost forgot: note that when you type “Button1.” and the list of button’s properties and methods pops up – you see the added “LastClickTime” as if it belongs to the standard Delphi’s TButton. Delphi (compiler) magic 🙂
Also note that you can use interceptor classes not only to extend VCL controls but to extend any Delphi class.
I think the word that is used mostly in Delphi literature is interposer class, not interceptor class.
Hi Stefan, thanks for the info. I’ve been calling this “interceptor” for ages – but yes, I agree “interposer” is also used – so will add that naming as well.
You write “You could think now of class helpers, but class helpers allow only class fields / methods to be added to a class.”.
Class helpers also allow instance methods to be added. They allow anything that does not require a difference in the memory layout of the class, i.e. no new instance fields, and no new virtual methods.
Hi Rudy, uops .. thanks for noticing – adjusted.
You should also mention that interposer / interceptor classes only work at runtime and you can’t use them to add any new functionality for designtime. Also the functionality added with interposer / interceptor class is not available to deriving classes.
Silvo, yes, I guessed this implies. Anyhow, for design time support one must create a proper custom control.