FindComponent() 未找到的组件
Components not being found by FindComponent()
我创建了一个登录表单,其中包含与 Access 文件中的用户名相对应的按钮。这些按钮是在 OnCreate
事件中创建的,因为我不想在每次显示屏幕时都必须创建它们。
按钮按预期显示,我已经创建了登录和注销过程,它们都按我预期的方式工作。
下一步是只为当前登录系统的用户显示按钮。为此,我在 OnActivate
事件中创建了以下代码:
procedure TUserInForm.FormActivate(Sender: TObject);
var
btn : TLBButton;
begin
With UserQuery do begin;
first;
while (not eof) do begin
BtnName := FieldByName('UserName').AsString;
Btn := TLBButton(FindComponent(BtnName));
if (Btn <> nil) then
if (FieldByName('LoggedIn').AsBoolean = True) then Btn.Visible := True else Btn.Visible := False;
next;
end;
end;
end;
但是,它找不到任何按钮 - 它们都是 nil
。如果我删除 nil
检查,代码将引发访问冲突异常。但是,在代码中的任何一点我都不会破坏按钮或表单本身。按钮存在,因为我可以在表单上看到它们。 BtnName
变量在单元内是全局变量。我已经检查 BtnName
变量是否从 table.
中正确填充
我以前用类似的代码找组件,没问题。事实上,我从另一个程序(有明显的变化)中“窃取”了上面显示的代码,它在其中运行良好。日志显示没有错误。
任何人都可以建议一些解决此问题的方法吗?很郁闷!
FindComponent()
搜索调用它的组件的拥有组件列表。我假设您的 OnCreate
处理程序创建了带有表单的按钮作为它们的 Owner
。但是 with
块将导致 FindComponent()
在 UserQuery
组件而不是表单上被调用。这可以解释为什么找不到按钮。
因此,您可以:
- 改用
Self.FindComponent()
,因为在表单上调用了OnActivate
处理程序,所以Self
将指向表单:
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;
- 完全摆脱
with
块,因为通常认为在非平凡情况下使用 with
是不好的做法(这种情况是原因的一个很好的例子):
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;
- 如果你想继续使用
with
块,你可以将按钮搜索移动到UserQuery
组件中不存在的单独功能,所以with
赢了' 不知道在哪个组件上调用函数:
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;
现在,话虽如此,将创建的按钮添加到您自己管理的列表而不是 VCL 代表您管理的列表中会是一个更好的设计。然后您将始终准确地知道在哪里可以找到按钮。例如:
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;
我创建了一个登录表单,其中包含与 Access 文件中的用户名相对应的按钮。这些按钮是在 OnCreate
事件中创建的,因为我不想在每次显示屏幕时都必须创建它们。
按钮按预期显示,我已经创建了登录和注销过程,它们都按我预期的方式工作。
下一步是只为当前登录系统的用户显示按钮。为此,我在 OnActivate
事件中创建了以下代码:
procedure TUserInForm.FormActivate(Sender: TObject);
var
btn : TLBButton;
begin
With UserQuery do begin;
first;
while (not eof) do begin
BtnName := FieldByName('UserName').AsString;
Btn := TLBButton(FindComponent(BtnName));
if (Btn <> nil) then
if (FieldByName('LoggedIn').AsBoolean = True) then Btn.Visible := True else Btn.Visible := False;
next;
end;
end;
end;
但是,它找不到任何按钮 - 它们都是 nil
。如果我删除 nil
检查,代码将引发访问冲突异常。但是,在代码中的任何一点我都不会破坏按钮或表单本身。按钮存在,因为我可以在表单上看到它们。 BtnName
变量在单元内是全局变量。我已经检查 BtnName
变量是否从 table.
我以前用类似的代码找组件,没问题。事实上,我从另一个程序(有明显的变化)中“窃取”了上面显示的代码,它在其中运行良好。日志显示没有错误。
任何人都可以建议一些解决此问题的方法吗?很郁闷!
FindComponent()
搜索调用它的组件的拥有组件列表。我假设您的 OnCreate
处理程序创建了带有表单的按钮作为它们的 Owner
。但是 with
块将导致 FindComponent()
在 UserQuery
组件而不是表单上被调用。这可以解释为什么找不到按钮。
因此,您可以:
- 改用
Self.FindComponent()
,因为在表单上调用了OnActivate
处理程序,所以Self
将指向表单:
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;
- 完全摆脱
with
块,因为通常认为在非平凡情况下使用with
是不好的做法(这种情况是原因的一个很好的例子):
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;
- 如果你想继续使用
with
块,你可以将按钮搜索移动到UserQuery
组件中不存在的单独功能,所以with
赢了' 不知道在哪个组件上调用函数:
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;
现在,话虽如此,将创建的按钮添加到您自己管理的列表而不是 VCL 代表您管理的列表中会是一个更好的设计。然后您将始终准确地知道在哪里可以找到按钮。例如:
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;