tags:

views:

173

answers:

6

Hi

I want to list name of all forms exist in my project in a ListBox Dynamically, then by clicking on each of them, list all buttons exist on that form in another ListBox.

But I don't know if it can be implemented and how it can.

Please help me.

Thanks

A: 

you can use "for" loop.

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;
sabri.arslan
Yes. But it returns assigned components only. I want to list all forms in my project included assigned and unassigned.
Hamid
I think, there is no way to find list of unassigned objects.
histrio
*Components* aren't assigned; *variables* are. Also, instead of `Application.Components` to get the list of forms, consider reading `Screen.Forms`.
Rob Kennedy
+1  A: 

There is no way (easy) to find the included forms.

But if you loop through the RCdata of the resources (See (1) (2) (3)), you can find the names of the forms. But that dosn't help you creating them.

In order to make forms "findable" the have to "register" them yourself, using RegisterCLass og finding them again using FindClass. See an example here: http://www.obsof.com/delphi_tips/delphi_tips.html#Button

BennyBechDk
Using RegisterClass would work, but unfortunately you can't iterate over the registered classes. So you have no way to fill the listbox. And FindClass takes a name as its parameter to return the corresponding class type. Without your own registry to register and later discover the registered form names, you're stuck.
Marjan Venema
Ah, I see now that you find the forms' names by going through the RCData. Missed that on first reading. Sorry.
Marjan Venema
Given the name and assumed that the class is not removed by the linker you can create such a form using "FindClass".
Uwe Raabe
+1  A: 

sabri.arslan's answer is the way to go to find all instantiated forms at run-time.

In the comments Hamid asked for a way to find unassigned forms as well. Assuming that by unassigned he means uninstantiated forms, there would be only one way to do so and that is to iterate over the registry of classes used by the vcl streaming system to instantiate components by name when a dfm is streamed in.

However, IIRC, forms are not automatically added to the registry. In fact, if you want to instantiate forms based on a string of their name, you need(ed) to add them to the class registry yourself. OP could of course do that for each of the forms in his project himself. But, that leaves the problem that the class registry used by the streaming system is implemented using var's in the implementation section of the classes unit. And therefore can't be iterated over (easily) from the outside.

So the solution would be to use the initialization section of all form units in the project and register each form in a "roll-your-own" registry with their name and class and have the registry provide the methods to iterate over the registered forms. These method can be used to populate the listbox mentioned by the OP.

To get at the TButtons on a form would then require instantiating the form (it could remain hidden) and iterating over the components using code similar to sabri.arslan's answer to find the TButton instances.

Instantiating the form would require getting the class of the form from the registry based on the form's name selected in the listbox.

Example of a simple roll-your-own form registry:

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.
Marjan Venema
Thank you very much dear Marjan! Your solution helped me very lot. I Kiss you!!!
Hamid
@Hamid: Instead of kissing me, would you consider accepting (click the check mark) this answer instead? :-)
Marjan Venema
+2  A: 

The forms are usually listed using Screen.Forms property, ex:

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;
Serg
+3  A: 

In case you are on Delphi 2010 you can use RTTI to list all registered ( = somehow used in the application) form classes:

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;

You must somehow take care that the class is not completely removed by the linker (therefor the registered hint above). Otherwise you cannot get hands on that class with the method described.

Uwe Raabe
A: 

Do you need this to be built at run time, or would compile time information work for you?

In recent versions (Delphi 2006 and higher?), you can set a compiler option to generate XML documentation for your project. A separate XML file is generated for each unit. You could parse this XML to find and forms and look at the members for any buttons.

Bruce McGee