This question already has answers here:
How do I make my GUI behave well when Windows font scaling is greater than 100%
(4 answers)
Closed 8 years ago.
Which do you think are best practices for making a windows dialog compatible both with standard fonts (96 dpi) and "large fonts" setting (120 dpi) so that objects don't overlap or get cut off?
BTW: Just in case it's relevant, I'm interested in doing this for Delphi dialogs.
Thanks in advance!
In general one should use layout managers for this purpose. That what they are designed for.
Delphi (did not work with it for a long time) does not have such managers but is able to handle different dpi ever since. You have to use the autosize propery of the components to ensure that they have the right size for the text they display. To prevent overlapping of components arrange them on the form using the alignment and anchor properties. Eventually you have to group components in containers to achieve a proper layout.
There's a pretty good article in the D2007 help file, under "Considerations When Dynamically Resizing Forms and Controls" (note that the URL is to the help file itself, and not a web page as such).
The same topic, under the same name, can be found in the D2010 help file (same caveat about the URL as above), or on the docwiki.
It also is worthwhile (at least a little bit) to examine TForm.Scaled and TForm.ScaleBy.
This is how I try to deal with Delphi VCL's pixels regardless of Window's font size setting.
unit App.Screen;
interface
uses Controls;
type
TAppScreen = class(TObject)
private
FDefaultPixelsPerInch: integer;
FPixelsPerInch: integer;
function GetPixelsPerInch: integer;
procedure SetPixelsPerInch(const Value: integer);
public
procedure AfterConstruction; override;
function DefaultPixelsPerInch: integer;
function InAcceptableRange(const aPPI: integer): boolean;
procedure ScaleControl(const aControl: TWinControl);
property PixelsPerInch: integer read GetPixelsPerInch write SetPixelsPerInch;
end;
TAppScreenHelper = class helper for TAppScreen
private
class var FInstance: TAppScreen;
class function GetInstance: TAppScreen; static;
public
class procedure Setup;
class procedure TearDown;
class property Instance: TAppScreen read GetInstance;
end;
implementation
uses
TypInfo, Windows, SysUtils, Forms, Graphics;
type
TScreenEx = class(TScreen)
published
property PixelsPerInch;
end;
TScreenHelper = class helper for TScreen
public
procedure SetPixelsPerInch(Value: integer);
end;
procedure TScreenHelper.SetPixelsPerInch(Value: integer);
begin
PInteger(Integer(Self) + (Integer(GetPropInfo(TScreenEx, 'PixelsPerInch').GetProc) and $00FFFFFF))^ := Value;
end;
procedure TAppScreen.AfterConstruction;
begin
inherited;
FDefaultPixelsPerInch := Screen.PixelsPerInch;
FPixelsPerInch := FDefaultPixelsPerInch;
end;
function TAppScreen.DefaultPixelsPerInch: integer;
begin
Result := FDefaultPixelsPerInch;
end;
function TAppScreen.GetPixelsPerInch: integer;
begin
Result := FPixelsPerInch;
end;
function TAppScreen.InAcceptableRange(const aPPI: integer): boolean;
begin
if DefaultPixelsPerInch > aPPI then
Result := DefaultPixelsPerInch * 0.55 < aPPI
else if DefaultPixelsPerInch < aPPI then
Result := DefaultPixelsPerInch * 1.55 > aPPI
else
Result := True;
end;
procedure TAppScreen.ScaleControl(const aControl: TWinControl);
begin
aControl.ScaleBy(PixelsPerInch, DefaultPixelsPerInch);
end;
procedure TAppScreen.SetPixelsPerInch(const Value: integer);
begin
FPixelsPerInch := Value;
Screen.SetPixelsPerInch(FPixelsPerInch);
end;
class function TAppScreenHelper.GetInstance: TAppScreen;
begin
if FInstance = nil then
FInstance := TAppScreen.Create;
Result := FInstance;
end;
class procedure TAppScreenHelper.Setup;
begin
TAppScreen.Instance;
end;
class procedure TAppScreenHelper.TearDown;
begin
FInstance.Free;
FInstance := nil;
end;
initialization
TAppScreen.Setup;
finalization
TAppScreen.TearDown;
end.
Try the following to test the effects of different pixels value:
TAppScreen.Instance.PixelsPerInch := 120;
TAppScreen.Instance.PixelsPerInch := 96;
TAppScreen.Instance.PixelsPerInch := 150;
You should change the PixelsPerInch before instantiate TForm's descendant including Delphi's VCL dialogs.
Never put a control and its describing label side by side, always put the label on top of it.
But apart from that? Maybe:
Leave enough space to the right and bottom of labels so they will not overlap with other controls when large fonts are used.
I have never tried using TLabeledEdit in that scenario, maybe they do that automatically?
There are purported commercial solutions (Developer Express VCL Layout Manager). But I do not trust any of them. I suspect that Embarcadero should address this as a critical weakness in the current UI component set (VCL).
I think that the third-party component set might be your fastest solution right now. It's commercial but not hugely expensive.
http://www.devexpress.com/products/VCL/ExLayoutControl/
Related
I am working at a huge, legacy source code where several SetFocus is called at many places, but sometimes, the check if the control is visible or enabled is missing.
Due to limited time, and the huge amount of source code, I decided that I want to ignore these errors, since the focus is (in our case) not a critical feature. A raised Exception will result in a complete failure, while a missing focus is just an optical issue.
My current plan is following:
I create an unit with a class helper like this:
type
TWinControlEx = class helper for TWinControl
procedure SetFocusSafe;
end;
procedure TWinControlEx.SetFocusSafe;
begin
if CanFocus then SetFocus;
end;
I include the unit to every unit which uses ".SetFocus" (I will use the global code search)
I replace every .SetFocus with .SetFocusSafe
There is a problem though: If possible, I want to avoid that coworkers accidently use .SetFocus , or forget to include the classhelper unit.
Which other options do I have?
The best case would be if there is a technique/hack to make SetFocus not raising an exception. (Without recompiling the VCL)
Just patch the TWinControl.SetFocus method:
unit SetFocusFix;
interface
implementation
uses
Controls,
Forms,
SysUtils,
Windows;
type
TWinControlHack = class(TWinControl)
public
procedure SetFocus; override;
end;
procedure TWinControlHack.SetFocus;
var
Parent: TCustomForm;
begin
if not CanFocus then Exit;
Parent := GetParentForm(Self);
if Parent <> nil then
Parent.FocusControl(Self)
else if ParentWindow <> 0 then
Windows.SetFocus(Handle)
else
ValidParentForm(Self);
end;
procedure RedirectFunction(OrgProc, NewProc: Pointer);
type
TJmpBuffer = packed record
Jmp: Byte;
Offset: Integer;
end;
var
n: UINT_PTR;
JmpBuffer: TJmpBuffer;
begin
JmpBuffer.Jmp := $E9;
JmpBuffer.Offset := PByte(NewProc) - (PByte(OrgProc) + 5);
if not WriteProcessMemory(GetCurrentProcess, OrgProc, #JmpBuffer, SizeOf(JmpBuffer), n) then
RaiseLastOSError;
end;
initialization
RedirectFunction(#TWinControl.SetFocus, #TWinControlHack.SetFocus);
end.
Alternatively
TWinControlEx = class helper for TWinControl
procedure SetFocus; reintroduce;
end;
with...
procedure TWinControlEx.SetFocus;
var
Parent: TCustomForm;
begin
if not CanFocus then Exit;
Parent := GetParentForm(Self);
if Parent <> nil then
Parent.FocusControl(Self)
else if ParentWindow <> 0 then
Winapi.Windows.SetFocus(Handle)
else
ValidParentForm(Self);
end;
My answer below does not answer DIRECTLY your question but it is still relevant because you rely on CanFocus. CanFocus returns a lie. You should not rely on it. The documentation is also wrong. More exactly, CanFocus can return True even if the control is not focusable. In this case an exception will be raised.
So, use this instead:
function CanFocus(Control: TWinControl): Boolean;
begin
Result:= Control.CanFocus AND Control.Enabled AND Control.Visible;
if Result
AND NOT Control.InheritsFrom(TForm)
then
{ Recursive call:
This control might be hosted by a panel which could be also invisible/disabled.
So, we need to check all the parents down the road, until we encounter the parent Form.
Also see: GetParentForm }
Result:= CanFocus(Control.Parent); { Parent of a control could be nil, but in this case Control.CanFocus will deal with that.}
end;
procedure SetFocus(Control: TWinControl);
begin
if CanFocus(Control)
then Control.SetFocus;
end;
PS: Under Lazarus CanFocus works properly.
Justification:
J provided a nice answer, but I don't like class helpers because if you have more than one class helper for the same class, the only one will be used. The process is almost "by dice": the order of the units in the "uses" clause determine which helper will apply. I don't like this amount of randomness in a programming language.
I want to allow a form to be sized by the user with its controls proportionaly resized. ScaleBy( N,M ) works fine for me but there are of course cumulative rounding errors if it is repeated. To counter this I simply wish to precede ScaleBy() with a call to recreate the form in its default appearance and size and then call ScaleBy with various values. I know I can do this by hosting my form within a panel (and disposing / recreating it) but is there a call that will reset the form after use of ScaleBy()?
Edit - I am using Delphi XE2 and would also be interested in anyone's success with a component or other code (paid or free) to scale a form neatly - my own downloads have not produced a working solution.
Try EasySize (TFormResizer) component.
The TFormResizer component resizes all of the controls on a form (or panel) when the form size changes.
I used it successfully years ago - works with D5/7. You might need to make small adjustments for XE2 (I do not have XE2, so I cant test it).
Usage:
uses
..., Easysize;
type
TForm1 = class(TForm)
...
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
private
FR: TFormResizer;
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
FR := TFormResizer.Create(Self);
FR.ResizeFonts := True;
FR.InitializeForm;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
FR.ResizeAll;
end;
end.
One solution would be to use the Components property of the form interate over all the child controls of a form and reset them back to their original value.
The following article has example code: http://delphi.about.com/od/adptips2005/qt/storecontrolpos.htm
This is for a slightly different purpose, but it shouldn't be to hard to modify the code to your needs.
First, adjust the scale to the original scale, then scale to new scale. For example, to scale a form in a OnResize event:
...
private
FDesignHeight: Integer;
FDesignWidth: Integer;
FPrevWidth: Integer;
end;
...
procedure TForm1.FormShow(Sender: TObject);
begin
FDesignHeight := Height;
FDesignWidth := Width;
FPrevWidth := Width;
Scaled := True;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
if Scaled then
begin
DisableAlign;
ScaleBy(FDesignWidth, FPrevWidth);
ScaleBy(Width, FDesignWidth);
EnableAlign;
end;
FPrevWidth := Width;
end;
procedure TForm1.FormCanResize(Sender: TObject; var NewWidth,
NewHeight: Integer; var Resize: Boolean);
begin
NewHeight := Round(NewWidth * FDesignHeight / FDesignWidth);
end;
This works fine for me. To solve rounding problems, I start with a base value of 100000;
Using a TSpinEdit with default value of '100' and steps of 10(%):
OnShow: OldScaleValue := 100000;
procedure TLogForm.SpinEdit1Change(Sender: TObject);
begin
DisableAlign;
try
Log('Org Width='+Width.ToString);
Scaleby(100000, OldScaleValue);
OldScaleValue := SpinEdit1.Value*1000;
Scaleby(OldScaleValue, 100000);
Log('NEW Width='+Width.ToString);
finally
EnableAlign;
end;
end;
This steps forward and backward with 10% increase / decrease without rounding issues.
I'm in the middle of a project with a number of child forms. Many of the forms may be open at once. I'd like to know if there's already something I can use to manage and keep track of these forms, much like the windows taskbar and/or task manager. If not, then what would be the best approach? I don't want to have to reinvent the wheel if this is already done.
Description
As mentioned above, this project has many forms which may be opened at once. I will also be implementing some visual list control (much like the taskbar or task manager) for user control of these forms (or in the user's case, the forms are called windows). The most ideal way to manage these would be to first capture each of these forms as they're created and keep record of them somewhere. Some forms need this behavior, and some forms do not. For example, modal forms will never need this handling.
I will be giving the user access to show, minimize, or close these forms, as well as some other future un-thought handling, like maybe a custom popup menu associated with one of these forms (but that's another subject). The point is, I need to build something to capture these forms and keep them in order.
This will also include some other user interaction with all the forms at once, as well as simple access to each one of them, similar to how Screen.Forms already works. For example, a command to minimize all forms (FormManager.MinimizeAll), to maximize the currently active form (FormManager.ActiveForm.Maximize), or with a particular form (FormManager[3].Maximize).
Possible Options
I understand there are a few far different approaches to accomplish similar results, and haven't started coding it yet because each of those approaches has a different starting point. The options are...
Wrap Screen.Forms and other associated functionality from the Screen (which wouldn't allow too much of my desired flexibility)
Every time I create a form, register it with this form manager (which is very flexible, but I have to make sure I always register each created form)
Build a master form to register its self with the form manager and inherit everything from it (which is also very flexible, but in different ways, and much more complex)
The second option is sounding the most promising so far. But again, I don't want to start building it if there is already a solution for this. I'm pretty confident that I'm not the first person to do this. I don't know how to search for such a thing, I get nothing related to what I want on Google.
The global variable Screen (in Forms unit) does some "tracking", ie
Screen.Forms list all currently open forms;
Screen.ActiveForm form which has input focus (see also FocusedForm);
Screen.OnActiveFormChange event;
You could add each form to a TObjectList. I wrote a component called FormStack, which allows you to add forms (even forms with the same name), retrieve, remove, etc. To get a Task Manager like behavior, I think you'd just need to iterate the list to obtain form names . Hopefully you can use something here to shed some light on your idea..
Here's the code for FormStack.
unit uFormstack;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Contnrs;
type
TFormstack = class(TComponent)
private
{ Private declarations }
FormList: TObjectList;
protected
{ Protected declarations }
public
{ Public declarations }
Constructor Create(AOwner: TComponent); Override;
Destructor Destroy; Override;
Procedure Add(InstanceClass: TComponentClass; Var Reference);
Procedure RemoveLast;
Procedure RemoveAll;
Function FindForm(AComponentClass: TComponentClass): Boolean;
Function GetForm(AComponentClass: TComponentClass): TObject;
Function GetByIndex(AIndex: Integer): TObject;
Procedure RemoveByIndex(AIndex: Integer);
published
{ Published declarations }
end;
procedure Register;
implementation
//{$R *.res}
procedure Register;
begin
RegisterComponents('FormStack', [TFormstack]);
end;
{-----------------------------------------------------------------------------
TFormStack
-----------------------------------------------------------------------------}
Constructor TFormStack.Create(AOwner: TComponent);
Begin
Inherited Create(AOwner);
FormList := TObjectList.Create;
FormList.OwnsObjects := True;
End;
Destructor TFormStack.Destroy;
Begin
FormList.Free;
Inherited Destroy;
End;
Procedure TFormStack.Add(InstanceClass: TComponentClass; Var Reference);
Var
Instance: TComponent;
Begin
Instance := TComponent(InstanceClass.NewInstance);
TComponent(Reference) := Instance;
Instance.Create(Self); // Owner is FormList <<-- blows up if datamodule in D2010
FormList.Add(Instance);
Instance.Tag := FormList.Count-1;
End;
Procedure TFormStack.RemoveAll;
Var
I: Integer;
Begin
For I := FormList.Count -1 downto 0 do // last in first out
begin
Self.RemoveLast;
End;
End;
// This removes the last form on the stack
Procedure TFormStack.RemoveLast;
Begin
if FormList.Count > 0 then
FormList.Remove(FormList.Items[FormList.Count-1]);
End;
Function TFormStack.FindForm(AComponentClass: TComponentClass): Boolean;
Var
I: Integer;
Begin
Result := False;
For I := FormList.Count-1 downto 0 do
If Formlist.Items[I].ClassType = AComponentClass then
Result := True;
End;
Function TFormStack.GetForm(AComponentClass: TComponentClass): TObject;
Var
I: Integer;
begin
Result := Nil;
For I := FormList.Count-1 downto 0 do
If Formlist.Items[I].ClassType = AComponentClass then
Result := FormList.Items[I];
end;
Function TFormStack.GetByIndex(AIndex: Integer): TObject;
begin
Result := Nil;
If FormList.Count-1 >= AIndex then
Result := FormList.Items[AIndex];
end;
Procedure TFormStack.RemoveByIndex(AIndex: Integer);
begin
If FormList.Count-1 >= AIndex then
FormList.Remove(FormList.Items[AIndex]);
end;
end.
If I understand you correctly, you want to track this in code while the app is running?
Maybe you can do something with Screen.Forms?
Aplogies for top posting. This has buggged me for years and now I really need to know the answer. I put 150 points bounty to attract interest, but could increase for the right answer.
Here is what I need:
I use the TMS obejct inpsctor from TMS Scripter Pro to allow users to design a form at run-time. You can assume that it derives from the standard Delphi Object Inspector and adds a little functionality but 90% of it just calls inherited methods.
When I click the ellipsis next to the Picture property of a TImage it calls an inherited method - I don't know which - to allow me to load a picture. When I have done so, I do not know which file the image was loaded from and I want to save it with the same name in a different directory.
I could prompt the user for a file name, but that looks confusing to him and is just plain sloppy.
I could find a TImage / TPicture descendant which stores the file name and allows me to query it. Anyone who can point out such a FOSS component gets the bounty.
I could derive from TPicture and or TImage and code it myself, but am not certain what to change. Anyone who can tell me exactly how to code it gets the the bounty. By exactly, I mean naming classes and methods.
Thanks in advance for any help. This one really annoys me.
Further to my previous question, which did not get a useful answer despite a bounty, I will try rephrasing the question.
Basically, when the user clicks the ellipsis in the object inspector, Delphi opens a file/open dialog. I want to replace this handling with my own, so that I can save the image's path.
I would have expected that all I need to do is to derive a class from TImage and override the Assign() function, as in the following code. However, when I do the assign function is never called. So, it looks like I need to override something else, but what?
unit my_Image;
interface
uses
Classes, ExtCtrls, Jpeg, Graphics;
type
Tmy_Image = class(Timage)
private
FPicture : TPicture;
protected
procedure OnChange(Sender: TObject);
public { Public declarations }
Constructor Create(AOwner: TComponent); override;
procedure SetPicture(picture : TPicture);
procedure Assign(Source: TPersistent); override;
published { Published declarations - available in the Object Inspector at design-time }
property Picture : TPicture read FPicture write SetPicture;
end; // of class Tmy_Image()
procedure Register;
implementation
uses Controls, Dialogs;
procedure Register;
begin
RegisterComponents('Standard', [Tmy_Image]);
end;
Constructor Tmy_Image.Create(AOwner: TComponent);
begin
inherited; // Call the parent Create method
Hint := 'Add an image from a file|Add an image from a file'; // Tooltip | status bar text
AutoSize := True; // Control resizes when contents change (new image is loaded)
Height := 104;
Width := 104;
FPicture := TPicture.Create();
self.Picture.Bitmap.LoadFromResourceName(hInstance, 'picture_poperty_bmp');
end;
procedure Tmy_Image.OnChange(Sender: TObject);
begin
Constraints.MaxHeight := Picture.Height;
Constraints.MaxWidth := Picture.Width;
Self.Height := Picture.Height;
Self.Width := Picture.Width;
end;
procedure Tmy_Image.SetPicture(picture : TPicture);
begin
MessageDlg('Tmy_Image.SetPicture', mtWarning, [mbOK], 0); // never called
end;
procedure Tmy_Image.Assign(Source: TPersistent);
begin
MessageDlg('Tmy_Image.Assign', mtWarning, [mbOK], 0); // never called
end;
end.
See if this works:
[...]
procedure Tmy_Image.PictureChange(Sender: TObject);
begin
ShowMessage('Should work');
end;
[...]
constructor Tmy_Image.Create(AOwner: TComponent);
begin
inherited; // Call the parent Create method
Hint := 'Add an image from a file|Add an image from a file'; // Tooltip | status bar text
AutoSize := True; // Control resizes when contents change (new image is loaded)
Height := 104;
Width := 104;
FPicture := TPicture.Create();
FPicture.OnChange := PictureChange; /// <<<<
self.Picture.Bitmap.LoadFromResourceName(hInstance, 'picture_poperty_bmp');
end;
[...]
God bless you!
Ok, I hacked the TMS code. It seemed to be the only way.
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.
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.
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;
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.
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;
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
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.