Почему мой компонент автоматически добавляет другие единицы в интерфейс использования?

Я пишу некоторые из своих собственных компонентов, некоторые из них просто производны от других компонентов, таких как TCustomButton, TCustomListBox и т.д.

Давайте скажем, что у меня есть TMyButton = class(TCustomButton), и это находится в модуле MyButton, я зарегистрировал этот компонент в пакете и установил его в среду IDE.

Теперь я создам новый пустой проект и отброшу TMyButton в форму. Когда я компилирую проект, он автоматически добавляет в раздел интерфейса эти единицы:

.., StdCtrls, MyButton;

Я ожидал, что MyButton, конечно, будет добавлен, но надеялся, что StdCtrls не будет.

Это не так плохо, но некоторые из моих других компонентов хуже, один из них, например, получен из TCustomActionMainMenuBar, и когда я добавляю это в свою форму и компилирую, я добавляю эти дополнительные единицы:

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

Одна из причин, по которой я хотел создать свои собственные компоненты, заключалась в том, чтобы предотвратить добавление столь большого количества имен узлов в раздел интерфейса, и я хотел сделать свою собственную картину и изменить для нее свойства по умолчанию и т.д.

К тому моменту, когда я добавлю 3 или 4 моих компонентов в форму, добавляются дополнительные имена в 6-10 единиц, и я не хочу, чтобы это произошло.

Итак, мой вопрос - возможно ли, чтобы IDE автоматически добавляла имена модулей в раздел интерфейса?

Тот факт, что у меня уже есть "нежелательные" имена модулей в интерфейсе фактических применений моего собственного источника компонентов, я думал, было бы достаточно. Мои компоненты знают, какие единицы им нужны, поэтому почему исходный файл формы должен знать/иметь возможность включать имена тоже?

Я просто хочу, чтобы MyButton, MyMenu; автоматически добавлен, а не все другие общие имена узлов, которые будут добавлены вместе с ними.

Ответ 1

Скорее всего, ваши компоненты извлекаются из других компонентов, которые зарегистрировали TSelectionEditor -производные реализации (см. RegisterSelectionEditor()), которые переопределяют виртуальный метод TSelectionEditor.RequiresUnits() для вставки необходимых единиц в предложения uses. Одной из причин этого является то, что эти компоненты определяют свойства/события, которые полагаются на типы в этих других единицах.

Ответ 2

TL;DR;

Невозможно предотвратить добавление этих единиц, и вам больше не нужно это делать.


Мои компоненты знают, какие единицы им нужны, поэтому зачем исходному файлу формы также знать имена?

Вы оба правы и неправы. Конечно, если код ограничен только созданием вашего компонента, тогда потребуется только единица, в которой объявлен этот компонент. Либо время выполнения, либо время разработки. Но когда код развивается и вы хотите внедрить обработчики событий, которые нуждаются в типах из блоков предков, тогда ваш код нуждается в этих единицах в предложении uses. Либо время выполнения, либо время разработки.

Пример: При отбрасывании TDBGrid из единицы DBGrids в форме также добавляется единица Grids, потому что, среди прочих, тип параметра State, TGridDrawState, опубликованного события OnDrawDataCell объявляется в блоке предка. Двойной щелчок по этому событию в конструкторе приводит к добавлению следующего обработчика:

procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
  Field: TField; State: TGridDrawState);
begin

end;

Теперь из-за наличия TGridDrawState этот исходный файл должен знать об устройстве Grids.

Заключение: может быть слишком много единиц, используемых для незначительной разработки, но всегда есть единицы, используемые для реализации всех опубликованных событий.


Я немного исследовал, как это работает. Я уже поддержал ответ Реми, потому что без него я бы не подумал об этом, но он на самом деле не совсем прав.

Рассмотрим следующие примеры единиц:

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;

Теперь вы добавляете компонент TAwLabelEx в форму, добавляются единицы AwLabel и AwLabelEx, и это происходит автоматически. Никакого особого участия не требуется. Блок AwLabel необходим для типа TAwLabelStyle. Обратите внимание, что это не имеет ничего общего с событиями в этом случае. Остается единственный аргумент в том, что этот тип используется в опубликованном разделе определения компонента.


Как насчет ISelectionEditor.RequiresUnits, как сказал Реми?

Рассмотрим, что мы перемещаем TAwLabelStyle в другую единицу:

unit AwTypes;

interface

type
  TAwLabelStyle = (bsWide, bsTall);

implementation

end.

Когда вы теперь отбрасываете компонент TAwLabel или TAwLabelEx в форме, блок AwTypes не добавляется. Процитировать по последней ссылке:

Примечание.. Возможно, что событие будет использовать тип с одним из его параметров, который не является ни в классе, ни в каком-либо из его предков. В этом случае редактор выбора, который реализует RequiresUnits, должен быть зарегистрирован и использоваться для каждого устройства, которое объявляет типы, необходимые для события.

Итак, зарегистрируйте редактор выбора:

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.

Отбрасывание компонента TAwLabel или TAwLabelEx в форме теперь приводит к добавлению блока AwTypes к предложению uses;

Ответ 3

Дерево наследования для TCustomActionMainMenuBar выглядит так:

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

Все единицы, которые появляются в этом дереве, будут втянуты в предложение uses, когда вы включаете компонент в форму +, будут повторно вставлены каждый раз при сохранении. Компоненты также могут включать в себя другие блоки, которые не содержат предка, используя API открытых инструментов, как говорили другие.

Ответ 4

Это происходит потому, что предки ваших компонентов используют Open Tools API для добавления некоторых единиц в предложение uses с таким кодом:

uses
  ToolsAPI;

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

Вы также можете найти этот вопрос.