Сегодня рассмотрим вопрос о том, как перечислить все компоненты на форме. Например, как очистить все TEdit одним циклом, как изменить надписи на всех TLabel, как нажать все TButton ![]()
В заметке про создание кнопок с использованием TImage я уже испольовал данный приём, теперь рассмотрим подробнее.
0. Матчасть
У объекта TForm (а также других, на которых можно поставить, например, кнопку) есть массив Components. Очевидно, так мы можем обращаться ко всем объектам на форме.
Количество объектов – свойство ComponentCount. Первый объект имеет индекс 0, последний ComponentCount-1.
1. Просто перечислим имена компонентов
Запишем в Memo, другой наглядной реализации в голову не пришло
var
i:integer;
begin
for i:=0 to ComponentCount-1 do
Memo1.Lines.Add(Components[i].Name);
end;
Теперь в Memo1 у нас имена всех компонентов.
2. Конкретизируем задачу
Будем использовать операторы is и as, тут без них никак:
var
i:integer;
begin
for i:=0 to ComponentCount-1 do
if Components[i] is TLabel then
Memo1.Lines.Add((Components[i] as TLabel).Caption);
end;
Вот так просто мы получили все надписи на объектах TLabel. Теперь давай сделаем их невидимыми – пусть никто не прочитает
var
i:integer;
begin
for i:=0 to ComponentCount-1 do
if Components[i] is TLabel then
(Components[i] as TLabel).Visible:=False;
end;
3. Итог
Теперь мы крутые, и нам не нужно прописывать:
Label1.Visible:=False; Label2.Visible:=False; ...
Наконец, хочу сделать картинку к этому посту в моём delphi блоге. Я поставил на форму в случайном порядке несколько TLabel и TEdit, поставил кнопку, по нажатию на которую написал:
procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
begin
for i:=0 to ComponentCount-1 do
if Components[i] is TLabel then
(Components[i] as TLabel).Caption:='http://parsers.info'
else if Components[i] is TEdit then
(Components[i] as TEdit).Text:='crystalbit';
end;
Запустил, нажал на неё. А результат смотри в начале этого поста
5. Замечание
Если используешь фреймы на форме, то этот механизм может не сработать.
Спасибо Алексею Тимохину
6. Вариант с рекурсией от JayDi
Подходит для фреймов
procedure FillChildComponentsList(const SourceComponent: TComponent;
var ChildsList: TList);
var
I: Integer;
begin
//рекурсивный поиск дочерних компонентов
for I := 0 to SourceComponent.ComponentCount - 1 do
begin
ChildsList.Add(SourceComponent.Components[I]);
FillChildComponentsList(SourceComponent.Components[I], ChildsList);
end;
end;
procedure Tf_TestFrameComponents.Button_TestComponentsClick(Sender: TObject);
var
FoundedComponentsList: TList;
I: Integer;
Comp: TComponent;
begin
//получение списка компонетов
FoundedComponentsList := TList.Create;
FillChildComponentsList(Self, FoundedComponentsList);
//смена текста у всех лейблов
for I := 0 to FoundedComponentsList.Count - 1 do
begin
Comp := FoundedComponentsList[I];
if Comp is TLabel then
begin
(Comp as TLabel).Caption := 'Its work!';
end;
end;
FreeAndNil(FoundedComponentsList);
end;
(c) crystalbit, http://parsers.info
а еще на блоге есть rss-лента!
P.S. сколько у меня мусора в метках, надо этим заняться

Имхо, такой обход всё же лучше делать рекурсивным.
Ведь, если на форме фрейм, на котором лежат компоненты, то такой код не тронет то, что на фрейме.
+ он также не сработает если компоненты создавались вручную в коде и у них Owner не форма.
Согласен, спасибо
А если компонент создавался динамически, и owner не форма (и не на форме), то как быть? И как такое может быть, если это визуальный компонент?
> А если компонент создавался динамически, и owner не форма (и не на форме), то как быть? И как такое может быть, если это визуальный компонент?
Если компонент создаётся например так:
var
tmpEdit: TEdit;
begin
tmpEdit:=TEdit.Create(Panel1); // это не корректно, но возможно
tmpEdit.Parent := Panel1;
end;
это да, Panel1 же на форме
Получается, рекурсией – наиболее полноценный алгоритм? С твоего позволения напишу пост об этом)
Panel1 – на форме, а tmpEdit на панели. Поэтому перебирая компоненты формы, мы не получим доступ к tmpEdit. Но реально – такая ситуация очень редка и скорее является извращённой.
Чаще же всего в реале с ней можно столкнуться только при использовании фреймов.
Не знаю, что там может быть на целый пост, Имхо, достаточно в этом посте указать, что для фреймов, этот механизм может не сработать.
всё понял
думал, что при создании, например, TLabel на TPanel в процессе дизайна, она также не будет в массиве Components, и что рекурсия – единственный выход
Теперь проверил и убедился, сейчас внесу изменения
Вот рабочий вариант с рекурсией (подходит для фреймов):
procedure FillChildComponentsList(const SourceComponent: TComponent; var ChildsList: TList); var I: Integer; begin //рекурсивный поиск дочерних компонентов for I := 0 to SourceComponent.ComponentCount - 1 do begin ChildsList.Add(SourceComponent.Components[I]); FillChildComponentsList(SourceComponent.Components[I], ChildsList); end; end; procedure Tf_TestFrameComponents.Button_TestComponentsClick(Sender: TObject); var FoundedComponentsList: TList; I: Integer; Comp: TComponent; begin //получение списка компонетов FoundedComponentsList := TList.Create; FillChildComponentsList(Self, FoundedComponentsList); //смена текста у всех лейблов for I := 0 to FoundedComponentsList.Count - 1 do begin Comp := FoundedComponentsList[I]; if Comp is TLabel then begin (Comp as TLabel).Caption := 'Its work!'; end; end; FreeAndNil(FoundedComponentsList); end;большое спасибо, внёс в пост
Параметр ChildsList: TList лучше объявить как const, чтобы случайно не изменить указатель.
Также хорошо бы использовать try .. finally.
procedure FillChildComponentsList(const SourceComponent: TComponent; const ChildsList: TList); var I: Integer; begin //рекурсивный поиск дочерних компонентов for I := 0 to SourceComponent.ComponentCount - 1 do begin ChildsList.Add(SourceComponent.Components[I]); FillChildComponentsList(SourceComponent.Components[I], ChildsList); end; end; procedure Tf_TestFrameComponents.Button_TestComponentsClick(Sender: TObject); var FoundedComponentsList: TList; I: Integer; Comp: TComponent; begin //получение списка компонетов FoundedComponentsList := TList.Create; try FillChildComponentsList(Self, FoundedComponentsList); //смена текста у всех лейблов for I := 0 to FoundedComponentsList.Count - 1 do begin Comp := FoundedComponentsList[I]; if Comp is TLabel then begin (Comp as TLabel).Caption := 'Its work!'; end; end; finally FreeAndNil(FoundedComponentsList); end; end;Как здорово, что все мы здесь сегодня собрались
Спасибо, с праздниками!
Очень полезная возможность.
Делал примерно таким образом перевод интерфейса.
Хорошее применение