tl;dr;
It is not possible to prevent the addition of those units, and you should not care about it any longer.
My components know which units they need, so why must the source file of a form need to know the names too?
You are both right and wrong. Of course, if the code is limited to only the creation of your component, then just the unit in which that component is declared would be needed. Either runtime and designtime. But when the code develops, and you want to implement event handlers that need types from the ancestor units, then your code needs those units in the uses clause. Either runtime and designtime.
Example: When dropping a TDBGrid
from the unit DBGrids
on a form, also the unit Grids
is added, because, among others, the type of the State
parameter, TGridDrawState
, of the published OnDrawDataCell
event is declared in the ancestor unit. Double clicking on that event in the designer results in the addition of the following handler:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
begin
end;
Now, due to the presence of TGridDrawState
, this source file needs to know of the Grids
unit.
Conclusion: there may be too much units used for minor development, but there are always enough units used for implementation of all published events.
I did a little research on how this really works. I already upvoted Remy's answer, because without it I would not have thought of doing so, but he actually is not completely right.
Consider the following example units:
unit AwLabel;
interface
uses
Classes, StdCtrls;
type
TAwLabelStyle = (bsWide, bsTall);
TAwLabel = class(TLabel)
private
FStyle: TAwLabelStyle;
published
property Style: TAwLabelStyle read FStyle write FStyle default bsWide;
end;
implementation
end.
unit AwLabelEx;
interface
uses
Classes, AwLabel;
type
TAwLabelEx = class(TAwLabel);
implementation
end.
unit AwReg;
interface
uses
AwLabel, AwLabelEx;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TAwLabel, TAwLabelEx]);
end;
Now, it you drop a TAwLabelEx
component on a form, the units AwLabel
and AwLabelEx
are added, and this happens automatically. No special involvement is needed. The AwLabel
unit is needed for the TAwLabelStyle
type. Note that it has nothing to do with events in this case. The only argument remaining is that the type is used in the published section of a component definition.
How about ISelectionEditor.RequiresUnits
as Remy told?
Consider we move TAwLabelStyle
to another unit:
unit AwTypes;
interface
type
TAwLabelStyle = (bsWide, bsTall);
implementation
end.
When you now drop a TAwLabel
or TAwLabelEx
component on the form, the AwTypes
unit is not added. To quote from the last link:
Note: It is possible that an event will use a type with one of its parameters that is neither in the class unit nor in any of its ancestor's units. In this case a selection editor that implements RequiresUnits should be registered and used for each unit that declares the types needed by the event.
So, let's register a selection editor:
unit AwReg;
interface
uses
Classes, AwTypes, AwLabel, AwLabelEx, DesignIntf, DesignEditors;
type
TAwLabelSelectionEditor = class(TSelectionEditor)
public
procedure RequiresUnits(Proc: TGetStrProc); override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TAwLabel, TAwLabelEx]);
RegisterSelectionEditor(TAwLabel, TAwLabelSelectionEditor);
end;
{ TAwLabelSelectionEditor }
procedure TAwLabelSelectionEditor.RequiresUnits(Proc: TGetStrProc);
begin
Proc('AwTypes');
end;
end.
Dropping a TAwLabel
or TAwLabelEx
component on a form now does result in the addition of the AwTypes
unit to the uses clause;