Почему мой компонент автоматически добавляет другие единицы в интерфейс использования?
Я пишу некоторые из своих собственных компонентов, некоторые из них просто производны от других компонентов, таких как 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);
Вы также можете найти этот вопрос.