Поиск ярлыка по заголовку
Я пытался выяснить, как искать ярлык по его Caption
:
for I := ComponentCount - 1 downto 0 do
begin
if Components[i] is TLabel then
if Components[i].Caption = mnNumber then
begin
Components[i].Left := Left;
Components[i].Top := Top + 8;
end;
end;
Я получаю ошибку: Undeclared identifier: 'Caption'
,
Как я могу решить эту проблему?
5 ответов
Последняя часть информации появилась в вашем комментарии к ответу Голеза: ваши ярлыки создаются во время выполнения, поэтому есть вероятность, что они не Form
как владелец. Вам нужно будет использовать Controls[]
массив для просмотра всех элементов управления, которые связаны с формой, и рекурсивного просмотра всех TWinControl
потомки, потому что они могут также содержать TLabel
"S.
Если вы собираетесь делать это и для разных типов элементов управления, вы, вероятно, захотите внедрить своего рода помощника, чтобы не повторяться слишком часто. Посмотрите на ответ Дэвида для готового решения, которое может включать некоторые "навороты", помимо решения проблемы под рукой; Например, возможность использовать анонимные функции для управления найденными элементами управления, а также возможность использовать анонимную функцию для фильтрации элементов управления по любым критериям.
Прежде чем вы начнете использовать такое сложное решение, вы, вероятно, должны понять самое простое. Очень простая рекурсивная функция, которая просто смотрит на все TControls
на все контейнеры, начиная с формы. Что-то вроде этого:
procedure TForm1.Button1Click(Sender: TObject);
procedure RecursiveSearchForLabels(const P: TWinControl);
var i:Integer;
begin
for i:=0 to P.ControlCount-1 do
if P.Controls[i] is TWinControl then
RecursiveSearchForLabels(TWinControl(P.Controls[i]))
else if P.Controls[i] is TLabel then
TLabel(P.Controls[i]).Caption := 'Test';
end;
begin
RecursiveSearchForLables(Self);
end;
Используя общий код Дэвида, вышеприведенное можно переписать так:
procedure TForm1.Button1Click(Sender: TObject);
begin
TControls.WalkControls<TLabel>(Self, nil,
procedure(lbl: TLabel)
begin
lbl.Caption := 'Test';
end
);
end;
Перебирая Components[]
это неправильный подход. Это просто дает компоненты, которые принадлежат форме. Вы пропустите любые компоненты, которые добавляются динамически и не принадлежат форме, или компоненты, которые принадлежат фреймам.
Вместо этого вы должны использовать Controls[]
, Тем не менее, это дает только детей первого поколения. Если существует более глубокая степень вложенности родителей / детей, вам необходимо выполнить повторную процедуру. Это больше работы. Я использую несколько помощников, чтобы сделать это легко. Я завернул их в этот блок:
unit ControlEnumerator;
interface
uses
System.SysUtils, System.Generics.Collections, Vcl.Controls;
type
TControls = class
private
type
TEnumerator<T: TControl> = record
FControls: TArray<T>;
FIndex: Integer;
procedure Initialise(WinControl: TWinControl; Predicate: TFunc<T, Boolean>);
class function Count(WinControl: TWinControl; Predicate: TFunc<T, Boolean>): Integer; static;
function GetCurrent: T;
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
TEnumeratorFactory<T: TControl> = record
FWinControl: TWinControl;
FPredicate: TFunc<T, Boolean>;
function Count: Integer;
function Controls: TArray<T>;
function GetEnumerator: TEnumerator<T>;
end;
public
class procedure WalkControls<T: TControl>(WinControl: TWinControl; Predicate: TFunc<T, Boolean>; Method: TProc<T>); static;
class function Enumerator<T: TControl>(WinControl: TWinControl; Predicate: TFunc<T, Boolean>=nil): TEnumeratorFactory<T>; static;
class function ChildCount<T: TControl>(WinControl: TWinControl; Predicate: TFunc<T, Boolean>=nil): Integer; static;
end;
implementation
{ TControls.TEnumerator<T> }
procedure TControls.TEnumerator<T>.Initialise(WinControl: TWinControl; Predicate: TFunc<T, Boolean>);
var
List: TList<T>;
Method: TProc<T>;
begin
List := TObjectList<T>.Create(False);
Try
Method :=
procedure(Control: T)
begin
List.Add(Control);
end;
WalkControls<T>(WinControl, Predicate, Method);
FControls := List.ToArray;
Finally
List.Free;
End;
FIndex := -1;
end;
class function TControls.TEnumerator<T>.Count(WinControl: TWinControl; Predicate: TFunc<T, Boolean>): Integer;
var
Count: Integer;
Method: TProc<T>;
begin
Method :=
procedure(Control: T)
begin
inc(Count);
end;
Count := 0;
WalkControls<T>(WinControl, Predicate, Method);
Result := Count;
end;
function TControls.TEnumerator<T>.GetCurrent: T;
begin
Result := FControls[FIndex];
end;
function TControls.TEnumerator<T>.MoveNext: Boolean;
begin
inc(FIndex);
Result := FIndex<Length(FControls);
end;
{ TControls.TEnumeratorFactory<T> }
function TControls.TEnumeratorFactory<T>.Count: Integer;
begin
Result := TEnumerator<T>.Count(FWinControl, FPredicate);
end;
function TControls.TEnumeratorFactory<T>.Controls: TArray<T>;
var
Enumerator: TEnumerator<T>;
begin
Enumerator.Initialise(FWinControl, FPredicate);
Result := Enumerator.FControls;
end;
function TControls.TEnumeratorFactory<T>.GetEnumerator: TEnumerator<T>;
begin
Result.Initialise(FWinControl, FPredicate);
end;
class procedure TControls.WalkControls<T>(WinControl: TWinControl; Predicate: TFunc<T, Boolean>; Method: TProc<T>);
var
i: Integer;
Control: TControl;
Include: Boolean;
begin
if not Assigned(WinControl) then begin
exit;
end;
for i := 0 to WinControl.ControlCount-1 do begin
Control := WinControl.Controls[i];
if not (Control is T) then begin
Include := False;
end else if Assigned(Predicate) and not Predicate(Control) then begin
Include := False;
end else begin
Include := True;
end;
if Include then begin
Method(Control);
end;
if Control is TWinControl then begin
WalkControls(TWinControl(Control), Predicate, Method);
end;
end;
end;
class function TControls.Enumerator<T>(WinControl: TWinControl; Predicate: TFunc<T, Boolean>): TEnumeratorFactory<T>;
begin
Result.FWinControl := WinControl;
Result.FPredicate := Predicate;
end;
class function TControls.ChildCount<T>(WinControl: TWinControl; Predicate: TFunc<T, Boolean>): Integer;
begin
Result := Enumerator<T>(WinControl, Predicate).Count;
end;
end.
Теперь вы можете решить вашу проблему следующим образом:
var
lbl: TLabel;
....
for lbl in TControls.Enumerator<TLabel>(Form) do
if lbl.caption=mnNumber then
begin
lbl.Left := Left;
lbl.Top := Top + 8;
end;
Или вы можете использовать предикат для проверки подписи внутри итератора:
var
Predicate: TControlPredicate;
lbl: TLabel;
....
Predicate := function(lbl: TLabel): Boolean
begin
Result := lbl.Caption='hello';
end;
for lbl in TControls.Enumerator<TLabel>(Form, Predicate) do
begin
lbl.Left := Left;
lbl.Top := Top + 8;
end;
ComponentCount только для подсчета. Используйте массив Компоненты, чтобы найти фактические компоненты. Для простоты вы можете поместить метку в переменную TLabel, что также позволит вам использовать специфичные для метки свойства, которые не видны в TComponent. Вы могли бы использовать with
для этого также, но я думаю, что это ухудшает читабельность.
var
l: TLabel;
for I := ComponentCount -1 downto 0 do
begin
if Components[i] is TLabel then // Check if it is.
begin
l := TLabel(Components[i]); // Typecast, to reach it's properties.
if l.Caption = mnNumber then
begin
l.Left := Left;
l.Top := Top +8;
end;
end;
end;
Компилятор не знает, что ваш Components[i] является TLabel.
Вы должны привести свой компонент к Tlabel следующим образом:
for I := ComponentCount - 1 downto 0 do
begin
if Components[i] is TLabel then //here you check if it is a tlabel
if TLabel(Components[i]).Caption = mnNumber then //and here you explicitly tell the
begin //compiler to treat components[i]
TLabel(Components[i]).Left := Left; //as a tlabel.
TLabel(Components[i]).Top := Top + 8;
end;
end;
Это необходимо, потому что компоненты [я] не знают заголовок.
Попробуй это:
для I:= ControlCount-1 до 0 сделать начать если Controls [i] - TLabel, тогда // Проверить, если это так. начать if (Управляет [i] как TLabel).Caption = mnNumber затем начать (Управляет [i] как TLabel).Left:= Left; (Управляет [i] как TLabel).Top:= Top +8; конец; конец; конец;