5

私はそのラベルでラベルを検索する方法を理解しようとしていました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'
この問題を解決するにはどうすればよいですか?

4

5 に答える 5

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;
于 2013-02-15T12:01:12.497 に答える
10

最後の情報は、Golezの回答に対するコメントに含まれています。ラベルは実行時に作成されるためForm、所有者としてのラベルがない可能性があります。配列を使用しControls[]て、フォームによって親になっているすべてのコントロールを確認し、すべてのTWinControl子孫にもTLabel'が含まれている可能性があるため、それらを再帰的に確認する必要があります。

この割り当てをさまざまな種類のコントロールに対して行う場合は、頻繁に繰り返さないように、何らかのヘルパーを実装することをお勧めします。手元の問題を解決するだけでなく、いくつかの「ベルとホイッスル」を含めることができる既製のソリューションについては、Davidの回答を見てください。匿名関数を使用して見つかったコントロールを操作する機能や、匿名関数を使用して任意の基準に基づいてコントロールをフィルタリングする機能と同様です。

このような複雑なソリューションを使い始める前に、おそらく最も単純なソリューションを理解する必要があります。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;
于 2013-02-15T12:41:16.510 に答える
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;
于 2013-02-15T10:38:35.247 に答える
1

コンパイラは、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;

これが必要なのは、components[i]がキャプションを認識していないためです。

于 2013-02-15T14:01:00.890 に答える
0

これを試して:

  for I:= ControlCount-1 downto 0 do
  始める
    Controls [i]がTLabelの場合、//そうであるかどうかを確認します。
    始める
      if(Controls [i] as TLabel).Caption = mnNumber then
      始める
        (Controls [i] as TLabel).Left:= Left;
        (Controls [i] as TLabel).Top:= Top +8;
      終わり;
    終わり;
  終わり;
于 2013-02-15T11:21:21.563 に答える