2013-02-15 23 views
5

Ja próbuje dowiedzieć się, jak szukać na etykiecie Jego Caption:Szukaj na etykiecie jego Podpis

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; 

pojawia się błąd: Undeclared identifier: 'Caption'.
Jak mogę rozwiązać ten problem?

+3

Dobrze. Jakie jest Twoje pytanie? – GolezTrol

+0

kiedy to robię, mówi napis jest niezadeklarowanym identyfikatorem –

+3

@Glen - To nie jest pytanie.

Odpowiedz

10

ostateczna informacja spadła na miejsce w swoim komentarzu do odpowiedzi Golez za: etykiety są tworzone w czasie wykonywania, więc istnieje szansa, że ​​nie mają Form jako właściciela. Będziesz musiał użyć tablicy Controls[], aby przejrzeć wszystkie formanty, które są nadrzędne przez formularz, i spojrzeć rekursywnie na wszystkich potomków TWinControl, ponieważ mogą one również zawierać TLabel.

Jeśli zamierzasz zrobić to za dużo i dla różnych rodzajów kontrolek, prawdopodobnie będziesz chciał zaimplementować jakiegoś pomocnika, aby nie powtarzać się zbyt często. Spójrz na odpowiedź Davida na gotowe rozwiązanie, które udaje się zawrzeć w kilku "dzwonkach i gwizdach", nie licząc rozwiązania problemu; Podobnie jak możliwość użycia anonimowych funkcji do manipulowania znalezionymi formantami, a także możliwość użycia anonimowej funkcji do filtrowania kontroli na podstawie dowolnych kryteriów.

Zanim zaczniesz używać tak skomplikowanego rozwiązania, powinieneś prawdopodobnie zrozumieć najprostszy. Bardzo prosta funkcja rekursywna, która po prostu patrzy na wszystkie TControls na wszystkie pojemniki, począwszy od formularza. Coś takiego:

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; 

Korzystanie kod rodzajowy Dawida, powyższe mogłyby zostać ponownie zapisać jako:

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    TControls.WalkControls<TLabel>(Self, nil, 
    procedure(lbl: TLabel) 
    begin 
     lbl.Caption := 'Test'; 
    end 
); 
end; 
+4

+1 Masz całkowitą rację, musisz zrozumieć to podejście, zanim rozważasz podejście bardziej ogólne. –

+0

Podoba mi się to podejście, ponieważ nie chcę dodawać wszystkich innych kodów odpowiedzi. Głównie ze względu na to jest to jednorazowe wyszukiwanie, które prawie nigdy nie będzie używane. Dzięki –

2

ComponentCount służy tylko do liczenia. Użyj tablicy Components, aby znaleźć rzeczywiste składniki. Dla ułatwienia możesz umieścić etykietę w zmiennej TLabel, która pozwoli ci również używać właściwości specyficznych dla etykiety, które nie są widoczne w TComponent. Do tego również można użyć with, ale myślę, że pogarsza to czytelność.

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

'Komponenty []' nie zawsze znajdą wszystkie etykiety w formularzu. Będzie brakować tych, które nie są własnością formularza. Musisz użyć 'Controls []'. –

+1

Dla prostego kodu takiego jak ten, najbezpieczniejszym zakładem jest "Components []", "Controls []" pominie wszystkie etykiety, które zostały upuszczone na panele! Jeśli używasz tylko projektanta i nie używasz Ramek, nie ma * możliwości * uzyskania Etykiety na formularzu bez znalezienia go za pomocą tablicy 'Składniki []'. 100% rozwiązaniem byłaby procedura rekursywna z użyciem 'Controls []', który wywołuje się dla wszystkich kontrolek 'TWinControl'. Dzięki temu etykiety będą wyświetlane na panelach, na ramkach, a nawet na etykietach tworzonych w środowisku wykonawczym bez rodzica. Nie sądzę, że rozwiązanie "Controls []" byłoby dobrą odpowiedzią na to pytanie. –

+0

@CosminPrund Naturalnie musisz powtarzać. Mam do tego pomocników. –

0

Spróbuj tego:

 
    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; 
+1

** Spowoduje to pominięcie wszystkich etykiet, które zostały upuszczone na kontenery, takie jak TPanel **! Tablica 'Controls []' wyświetla tylko elementy sterujące, które są nadrzędne przez podany 'TWinControl'. Musisz podać funkcję rekursywną, która analizuje wszystkie dzieci "TWinControl", aby to było rozwiązaniem. –

+2

niezły. AS jest drogim operatorem. Jeśli go użyjesz (nadmiar po operatorze IS), użyjesz go tylko raz, a nie trzy razy z rzędu. –

+2

Lepiej po prostu typecast "TLabel (Controls [i]). Napisy" lub użyj zmiennej lokalnej zaimplementowanej przez [@GolezTrol] (http://stackoverflow.com/a/14892866/937125). – kobik

13

iteracji nad Components[] jest złe podejście. To po prostu daje składniki, które są własnością formularza. Pominiesz wszystkie komponenty dodane dynamicznie, a nie będące własnością formularza lub komponenty należące do ramek.

Zamiast tego należy użyć Controls[]. Jednak to tylko daje dzieciom pierwszej generacji. Jeśli istnieje głębsze zagnieżdżanie się rodzica/dziecka, wówczas trzeba się powtórzyć. To więcej pracy. Używam niektórych pomocników, aby to ułatwić. Mam je zawinięte w tej jednostce:

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. 

Teraz możesz rozwiązać swój problem tak:

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; 

Lub można skorzystać z predykatu umieścić test napisów wewnątrz iteracyjnej:

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; 
+0

spróbuję później ... o tym, kiedy mam zamknąć dzień: D –

+2

+1 Podoba mi się, w jaki sposób używasz tej samej metody do zliczania kontrolek i ekstrakcji kontrolek. Podoba mi się użycie 'TControlPredicate' do filtracji. –

+0

@CosminPrund Dziękuję. Teraz zamieniam to wszystko na generyczne typy. Znacznie czystsze. –

1

Kompilator nie wie komponentów [i] oznacza TLabel.

Trzeba oddać swój komponent do TLabel tak:

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; 

Jest to konieczne, ponieważ komponenty [i] nie zna podpis.