我试图弄清楚如何通过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'
。
我该如何解决这个问题?
答案 0 :(得分:13)
迭代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;
答案 1 :(得分:10)
最后一条信息在您对Golez的回答的评论中落实到位:您的标签是在运行时创建的,因此他们有可能没有Form
作为所有者。您需要使用Controls[]
数组来查看表单父级的所有控件,并递归查看所有TWinControl
个后代,因为它们可能还包含TLabel
个。
如果您要进行此分配以及针对不同类型的控件,您可能希望实现某种帮助,这样您就不会经常重复自己。看看大卫的答案,为现成的解决方案设法包括一些“花里胡哨”,除了解决手头的问题;就像使用匿名函数来操作找到的控件的能力一样,它能够使用匿名函数根据任何条件过滤控件。
在开始使用如此复杂的解决方案之前,您应该了解最简单的解决方案。一个非常简单的递归函数,它只是从表单开始查看所有容器上的所有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;
使用David的通用代码,上面的内容可以重写为:
procedure TForm1.Button1Click(Sender: TObject);
begin
TControls.WalkControls<TLabel>(Self, nil,
procedure(lbl: TLabel)
begin
lbl.Caption := 'Test';
end
);
end;
答案 2 :(得分:2)
ComponentCount仅用于计数。使用Components数组查找实际组件。为方便起见,您可以将标签放在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;
答案 3 :(得分:1)
编译器不知道您的组件[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;
这是必需的,因为components [i]不知道标题。
答案 4 :(得分:0)
试试这个:
for I := ControlCount-1 downto 0 do begin if Controls[i] is TLabel then // Check if it is. begin if (Controls[i] as TLabel).Caption = mnNumber then begin (Controls[i] as TLabel).Left := Left; (Controls[i] as TLabel).Top := Top +8; end; end; end;