动态列出项目中的所有表单。

4
我希望能在一个动态的ListBox中列出我项目中存在的所有表单的名称,然后通过点击每个表单,在另一个ListBox中列出该表单中存在的所有按钮。
但是我不知道这是否可以实现以及如何实现。
6个回答

7

如果您使用的是Delphi 2010,您可以使用RTTI列出所有已注册(即在应用程序中以某种方式使用)的窗体类:

uses
  TypInfo, RTTI;

procedure ListAllFormClasses(Target: TStrings);
var
  aClass: TClass;
  context: TRttiContext;
  types: TArray<TRttiType>;
  aType: TRttiType;
begin
  context := TRttiContext.Create;
  types := context.GetTypes;
  for aType in types do begin
    if aType.TypeKind = tkClass then begin
      aClass := aType.AsInstance.MetaclassType;
      if (aClass <> TForm) and aClass.InheritsFrom(TForm) then begin
        Target.Add(aClass.ClassName);
      end;
    end;
  end;
end;

你必须确保类没有被链接器完全删除(因此上面有“registered”提示)。否则,你将无法使用所描述的方法获取该类。


Uwe Raabe,这行代码:
context := TRttiContext.Create 是不必要的,对吧?它是一个记录类型。
- Rafael Piccolo
@Rafael:不,Create实际上是需要用来初始化上下文的。否则,你就会得到一个未初始化的记录。 - Uwe Raabe

6
表格通常使用Screen.Forms属性列出,例如:
procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;

begin
  Memo1.Lines.Clear;
  for I:= 0 to Screen.CustomFormCount - 1 do
    Memo1.Lines.Add(Screen.Forms[I].Caption);
end;

4

sabri.arslan的回答是在运行时查找所有已实例化表单的方法。

在评论中,Hamid要求找到未分配的表单。假设他指的是未实例化的表单,那么唯一的方法就是遍历VCL流媒体系统使用的类注册表,通过名称实例化组件,当DFM被流式传输时。

然而,我记得,表单并没有自动添加到注册表中。事实上,如果您想根据名称字符串实例化表单,您需要自己将它们添加到类注册表中。OP当然可以为项目中的每个表单都这样做。但是,流系统使用的类注册表是使用类单元的实现部分中的var实现的。因此,不能从外部轻松地对其进行迭代。

所以解决方案是使用项目中所有表单单元的初始化部分,并在一个“自定义”注册表中注册每个表单及其名称和类,并使注册表提供迭代已注册表单的方法。可以使用这些方法来填充OP提到的列表框。

要获取表单上的TButton,就需要实例化表单(它可以保持隐藏),并使用类似于sabri.arslan的答案的代码遍历组件,以查找TButton实例。

实例化表单需要从注册表中根据列表框中选择的表单名称获取表单类。

以下是一个简单的自定义表单注册表示例:

unit Unit1;

interface

uses
  Classes
  , Forms
  , SysUtils
  ;

  procedure RegisterForm(aName: string; aClass: TFormClass);
  procedure ListForms(aNames: TStrings);
  function InstantiateForm(aName: string): TCustomForm;

implementation

var
  FormRegistry: TStringList;

procedure RegisterForm(aName: string; aClass: TFormClass);
begin
  FormRegistry.AddObject(aName, Pointer(aClass));
end;

procedure ListForms(aNames: TStrings);
var
  i: Integer;
begin
  for i := 0 to FormRegistry.Count - 1 do begin
    aNames.Add(FormRegistry[i]);
  end;
end;

function InstantiateForm(aName: string): TCustomForm;
var
  idx: Integer;
  frmClass: TFormClass;
begin
  Result := nil;
  idx := FormRegistry.IndexOf(aName);
  if idx > -1 then begin
    frmClass := TFormClass(FormRegistry.Objects[idx]);
    Result := frmClass.Create(nil);
  end;
end;

initialization
  FormRegistry := TStringList.Create;
  FormRegistry.Duplicates := dupError;
  FormRegistry.Sorted := True;
finalization
  FreeAndNil(FormRegistry);
end.

3
@Hamid:你可以考虑接受(点击复选框)这个答案,而不是亲我吗? :-) - Marjan Venema

0
你可以使用“for”循环。
procedure ListForms(lbForms:TListBox);
var
  i,j:integer;
begin
         for i:=0 to application.ComponentCount-1 do
          if application.components[i] is tform then
          begin
           lbForms.add(tform(application.components[i]).Name);
          end;
end;

procedure ListBox1Click(Sender:TObject);
var
 ix,j,i:integer;
begin
 ix:=ListBox1.ItemIndex;
 if ix>=0 then
 begin
  for i:=0 to application.componentcount-1 do
   if application.components[i] is tform then
   begin
    if tform(application.components[i]).name=listbox1.items.strings[ix] then
    begin
     for j:=0 to tform(application.components[i]).controlcount - 1 do
      if tform(application.components[i]).controls[i] is tbutton then
      begin
       listbox2.add(tbutton(tform(application.components[i]).controls[i]).caption);
      end;
     break;
    end;
   end;
 end;
end;

是的。但它只返回已分配的组件。我想列出我的项目中包括已分配和未分配的所有窗体。 - Hamid
我认为,没有办法找到未分配对象的列表。 - histrio
1
组件 不被分配; 变量 被分配。另外,考虑使用 Screen.Forms 读取表单列表,而不是 Application.Components - Rob Kennedy

0

很难(容易)找到包含的表单。

但是,如果您循环遍历资源的RCdata(请参见(1) (2) (3)),则可以找到表单的名称。但这并不能帮助您创建它们。

为了使表单“可查找”,您必须自己“注册”它们,使用RegisterCLass或使用FindClass再次找到它们。在此处查看示例:http://www.obsof.com/delphi_tips/delphi_tips.html#Button


使用RegisterClass可以工作,但不幸的是,您无法迭代已注册的类。因此,您无法填充列表框。FindClass以名称作为其参数返回相应的类类型。如果没有自己的注册表来注册和稍后发现已注册的窗体名称,则会陷入困境。 - Marjan Venema
啊,我现在明白了,你是通过遍历RCData来找到表单名称的。第一次阅读时错过了这一点。抱歉。 - Marjan Venema
假设类没有被链接器移除,您可以使用“FindClass”来创建这样的表单,给定类的名称。 - Uwe Raabe

0
你需要在运行时构建这个程序,还是编译时的信息对你有用?
在最近的版本中(Delphi 2006及更高版本?),你可以设置编译器选项来为你的项目生成XML文档。每个单元都会生成一个单独的XML文件。你可以解析这个XML文件来查找窗体并查看任何按钮的成员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接