12

I have been writing some of my own custom components, some are simply derived from other components such as TCustomButton, TCustomListBox etc.

Lets say then I have TMyButton = class(TCustomButton) and this is in a unit named MyButton, I have registered this component in a packaged and installed into the IDE.

Now I will create a new empty Project and drop a TMyButton to the form. When I compile the project it automatically adds to the interface section these units:

.., StdCtrls, MyButton;

I was expecting MyButton of course to be added, but had hoped StdCtrls was not.

That is not so bad, but some of my other components are worse, one for example is derived from TCustomActionMainMenuBar and when I add that to my form and compile I get these extra units added:

.., ToolWin, ActnMan, ActnCtrls, ActnMenus, MyMenu;

One of the reasons I wanted to create my own components was to prevent so many unit names been added to the interface section, that and I wanted to do my own painting and change default properties etc to them.

By the time I add 3 or 4 of my components to the form, an extra 6-10 unit names are been added automatically and I dont want this to happen.

So my question - is it possible to prevent the IDE from automatically adding unit names to the interface section?

The fact that I already have the "unwanted" unit names in the actual uses interface of my own components source I thought would have been sufficient. My components know which units they need, so why must the source File of a form need to know / be allowed to included the names too?

I just want MyButton, MyMenu; automatically added, not all the other common unit names to be added with them.

  • I think they need to be added because your components are depending on them - will it work if you manually remove those units? Will it still compile? – Jeff Feb 02 '13 at 09:10
  • @Jeff if I remove them they just come back on next compile. The fact those units are already inside my components source I thought would be enough for Delphi to know not add to them to the form unit. –  Feb 02 '13 at 09:11
  • What if you try to create the component through code (myButton := TMyButton.Create(); ), having only your own units included, then what? – Jeff Feb 02 '13 at 09:14
  • 2
    @Jeff Of course that won't make them magically appear. The whole purpose of what OP is doing is to have visual controls dropped from the palette. – Jerry Dodge Feb 02 '13 at 14:14
  • @Jeff: No, they won't magically appear if the controls are created in code. The code either won't compile or you'll get exceptions at runtime. The IDE wouldn't put the units there if they weren't needed. – Ken White Feb 02 '13 at 16:35
  • The reason I asked was that if it would compile, and run, without errors, then I would think it would be the IDE's fault, since it wasn't smart enough to determine if all the required units are present. – Jeff Feb 03 '13 at 20:27

4 Answers4

10

Chances are that your components are deriving from other components that have registered TSelectionEditor-derived implementations (see RegisterSelectionEditor()) that override the virtual TSelectionEditor.RequiresUnits() method to insert required units into uses clauses. One reason to do this is if those components define properties/events that rely on types in those other units.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • +1 You hit the nail with that last sentence, but it [doesn't always require](http://stackoverflow.com/a/14665835/757830) a selection editor implementation to add units to the uses clause. – NGLN Feb 02 '13 at 20:03
  • Actually, you proved in your answer with examples the point I was trying to explain about when TSelectionEditor is needed without resorting to OTA. I know how TSelectionEditor works, because I had to create a few implementations myself for use in Indy. – Remy Lebeau Feb 03 '13 at 01:12
  • With all respect, you didn't (try to) explain about when a TSelectionEditor is needed, and I didn't comment you to explain how TSelectionEditor works (I assumed you knew). The point I was trying to make is that neither TSelectionEditor nor OTA are necessary to get units added to the uses clause, while your answer states that it does. Or better: doesn't explain why units are added without a specific TSelectionEditor implementation. – NGLN Feb 03 '13 at 15:20
  • (On the other hand, chances are that the IDE internally indeed uses RegisterSelectionEditor, probably as part of the routine RegisterComponentsProc points to, but it's Borland magic, and we simply don't know for sure.) – NGLN Feb 03 '13 at 15:21
  • I tried with some other 3rd party components and they all add extra unit references too. I guess I should do nothing to try and change it as the IDE insists on adding them. I would have liked to keep the uses list minimal of too many references but never mind. –  Feb 05 '13 at 13:21
9

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;

Community
  • 1
  • 1
NGLN
  • 43,011
  • 8
  • 105
  • 200
4

The inheritence tree for TCustomActionMainMenuBar looks like this:

System.Classes.TObject
System.Classes.TPersistent
System.Classes.TComponent
Vcl.Controls.TControl
Vcl.Controls.TWinControl
Vcl.ToolWin.TToolWindow
Vcl.ActnMan.TCustomActionBar
Vcl.ActnCtrls.TCustomActionDockBar
Vcl.ActnMenus.TCustomActionMenuBar
Vcl.ActnMenus.TCustomActionMainMenuBar
Vcl.ActnMenus.TActionMainMenuBar

All the units that appear in this tree will get drawn into the uses clause when you include the component on the form + will be re-inserted every time you save. Components can also include other units that do not contain an ancestor by using the Open Tools API as others have said.

Dan Bartlett
  • 826
  • 7
  • 8
2

This is happening because ancestors of your components are using Open Tools API to add some units to the uses clause with a code like this:

uses
  ToolsAPI;

var
  currentProject: IOTAProject;
begin
  currentProject := GetActiveProject();
  currentProject.AddFile('StdCtrls.pas', True);

You might also find this question interesting.

Community
  • 1
  • 1
iMan Biglari
  • 4,674
  • 1
  • 38
  • 83
  • The question is about source files, but this answer addresses project files. – NGLN Feb 02 '13 at 09:40
  • @NGLN Thanks for the edit. To be honest, I know almost nothing about `OTA`. – iMan Biglari Feb 02 '13 at 09:46
  • 4
    It is not just OTA that causes this. It is simplier for a component to use `RegisterSelectionEditor()` to register a `TSelectionEditor`-derived class that overrides the virtual `RequiresUnits()` method. That also affects the units that the IDE inserts into `uses` clauses. – Remy Lebeau Feb 02 '13 at 10:43