FindComponent()
searches the owned-components-list of the component that it is called on. I’m assuming your OnCreate
handler creates the buttons with the Form as their Owner
. But the with
block will cause FindComponent()
to be called on the UserQuery
component instead of the Form. That would explain why the buttons are not being found.
So, you can either:
- use
Self.FindComponent()
instead, since the OnActivate
handler is being called on the Form, so Self
will point at the Form:
procedure TUserInForm.FormActivate(Sender: TObject);
var
Btn : TLBButton;
BtnName : string;
begin
with UserQuery do begin
First;
while (not Eof) do begin
BtnName := FieldByName('UserName').AsString;
Btn := TLBButton(Self.FindComponent(BtnName));
if (Btn <> nil) then
Btn.Visible := FieldByName('LoggedIn').AsBoolean;
Next;
end;
end;
end;
- get rid of the
with
block altogether, as it is generally considered to be bad practice to use with
anyway in non-trivial cases (this situation is a good example of why):
procedure TUserInForm.FormActivate(Sender: TObject);
var
Btn : TLBButton;
BtnName : string;
begin
UserQuery.First;
while (not UserQuery.Eof) do begin
BtnName := UserQuery.FieldByName('UserName').AsString;
Btn := TLBButton(FindComponent(BtnName));
if (Btn <> nil) then
Btn.Visible := UserQuery.FieldByName('LoggedIn').AsBoolean;
UserQuery.Next;
end;
end;
- if you want to keep using the
with
block, you can move the button search to a separate function that does not exist in the UserQuery
component, so the with
won’t be confused about which component to call the function on:
procedure TUserInForm.FormActivate(Sender: TObject);
var
Btn : TLBButton;
BtnName : string;
begin
with UserQuery do begin
First;
while (not Eof) do begin
BtnName := FieldByName('UserName').AsString;
Btn := FindButton(BtnName);
if (Btn <> nil) then
Btn.Visible := FieldByName('LoggedIn').AsBoolean;
Next;
end;
end;
end;
function TUserInForm.FindButton(const BtnName: string): TLBButton;
begin
Result := TLBButton(FindComponent(BtnName));
end;
Now, that being said, it would be a better design to add the created buttons to a list that you manage for yourself, rather than a list that the VCL manages on your behalf. Then you will always know exactly where to find the buttons. For example:
type
TUserInForm = class(TForm)
UserQuery: TQuery;
...
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
...
private
Buttons: TList;
function FindButton(const BtnName: string): TLBButton;
...
end;
...
procedure TUserInForm.FormCreate(Sender: TObject);
var
Btn : TLBButton;
begin
Buttons := TList.Create;
...
Btn := TLBButton.Create(Self);
Btn.Name := ...
Buttons.Add(Btn);
...
end;
procedure TUserInForm.FormDestroy(Sender: TObject);
begin
Buttons.Free;
end;
procedure TUserInForm.FormActivate(Sender: TObject);
var
Btn : TLBButton;
BtnName : string;
begin
UserQuery.First;
while (not UserQuery.Eof) do begin
BtnName := UserQuery.FieldByName('UserName').AsString;
Btn := FindButton(BtnName);
if (Btn <> nil) then begin
Btn.Visible := UserQuery.FieldByName('LoggedIn').AsBoolean;
end;
UserQuery.Next;
end;
end;
function TUserInForm.FindButton(const BtnName: string): TLBButton;
var
i: integer;
begin
for i := 0 to Buttons.Count-1 do begin
Result := TLBButton(Buttons[i]);
if Result.Name = BtnName then Exit;
end;
Result := nil;
end;
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…