FindComponent() 找不到组件

Cap*_*fka -1 delphi delphi-5

我创建了一个登录表单,其中包含与 Access 文件中保存的用户名相对应的按钮。按钮是在OnCreate事件中创建的,因为我不想每次显示屏幕时都必须创建它们。

按钮按预期显示,并且我创建了 LogOn 和 LogOff 过程,它们都按我的预期工作。

下一步是仅显示当前登录到系统的用户的按钮。为此,我在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;
Run Code Online (Sandbox Code Playgroud)

但是,它没有找到任何按钮——它们都是nil. 如果我删除nil检查,代码将引发访问冲突异常。但是,在代码中的任何一点我都不会破坏按钮或表单本身。按钮存在,因为我可以在表单上看到它们。该BtnName变量在单元内是全局的。我已经检查过该BtnName变量是否从表中正确填充。

我以前使用过类似的代码来查找组件,没有任何问题。事实上,我从另一个运行良好的过程(有明显的变化)“窃取”了上面显示的代码。日志显示没有错误。

任何人都可以提出一些解决此问题的方法吗?这很令人沮丧!

Rem*_*eau 8

FindComponent()搜索它被调用的组件的拥有组件列表。我假设您的OnCreate处理程序使用 Form 作为它们的Owner. 但是with块会导致FindComponent()UserQuery组件而不是表单上被调用。这将解释为什么找不到按钮。

因此,您可以:

  • Self.FindComponent()改为使用,因为OnActivate处理程序是在 Form 上调用的,所以Self将指向 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;
Run Code Online (Sandbox Code Playgroud)
  • 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;
Run Code Online (Sandbox Code Playgroud)
  • 如果您想继续使用该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;
Run Code Online (Sandbox Code Playgroud)

现在,话虽如此,将创建的按钮添加到您自己管理的列表中会是一个更好的设计,而不是 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;
Run Code Online (Sandbox Code Playgroud)

  • [应该知道“with”的问题](/sf/ask/1630618671/)OP。 (3认同)