Delphi - child forms have frame of parent - delphi

I have a delphi application with multiple forms. Initially I had tried a setup where each newly opened form was a frame and the "parent" of this form (whichever called to open the form) was hidden as the child was shown with the child being resized and relocated to give a seamless effect of having one window, when the child is closed the parent is relocated and again made visible. All forms have a bsSingle border style for the Windows title block.
This approach worked well for positioning however the issue I have is a noticeable flicker as the parent form is closed and the child opened, and as there is a small time period where no form is opened the icon/tray on the start bar would shift around and itself become hidden and visible.
Does anybody have any advice on solving this problem? I thought perhaps if I only had one form with the border within the application and opened each new form within this border it would work better - though I am unsure how exactly to do this.
Any help is much appreciated.

It is easy to make one form appear as a child inside another. Create a new form which will contain and create your other forms:
procedure TMainForm.FormCreate(Sender: TObject);
var
F : TForm;
begin
F := TOneOfYourChildForms.Create(Self);
F.Parent := Self;
F.Show();
end;
Create both your child forms similar to this, then just do Show on the one you want to display and Hide on the other. Set BorderStyle to bsNone on the child forms to remove the caption. Turn off Auto-Create on your forms in project settings if you create them yourself like this instead.

I've had success with this design, and I think it helped to have the contents of the "main form" within a TFrame as well. When you want to show the main form, you would just perform a frame swap.

Related

Handle Needed For Firemonkey

How can i set the handle needed property for a form in firemonkey. In normal delphi I use to create forms inside other components at run time. IE:
Form1 := TForm1.Create(Panel1);
Form1.Parent := Panel1;
Form1.HandleNeeded;
But now in Firemonkey there are no Handles per say. So is there another way i can do this.
It is pretty essential that it has the parent Panel1 as the form has to only show in panel1 and no where else on the screen
In FMX if you want one form to be displayed inside another:
On the child form, add any controls inside a container (e.g. a TLayout). Create the child form. Set the Parent property of the TLayout (etc.) to the parent form (or, more probably a container on the parent form so you can set the child TLayout's alignment to alClient).
Thats what i am trying, but the form still apears as an independet form. not in the layout:
TNewLogin:=TFrmLogin.Create(Self);
TNewLogin.Parent:=Layout1;
TNewLogin.Show;

How to detach a panel and show it in a separate window?

Let's say I have form A that contains a panel (with many other controls in it) and a form B that it is empty.
Can I programmatically detach the panel from form A and move it in form B (and maybe back to form A)?
I know that I can change the Owner of the panel but does it work between different forms?
Update:
After some Googling I see that there is a ParentWindow property.
As noted by others, there are several problems with changing the parent window of a control without changing the ownership, and changing a controls owner can be difficult if it has multiple controls sitting on it...
One way around it is to use a frame instead. A frame owns all of it's sub-controls, so all you would need to do is change the owner and parent of the frame, and everything else will come along with it. This approach also allows you to keep all the event handlers and glue code in a single place as well.
N#
You have to take ownership into account, otherwise the destruction of form A would lead to the disappearance (i.e. destruction) of your panel on form B, or worse.
type
TForm2 = class(TForm)
public
InsertedPanel: TControl; // or TPanel
.
procedure RemoveComponents(AForm: TComponent; AControl: TWinControl);
var
I: Integer;
begin
for I := 0 to AControl.ControlCount - 1 do
begin
if AControl.Controls[I] is TWinControl then
RemoveComponents(AForm, TWinControl(AControl.Controls[I]));
if AControl.Controls[I].Owner = AForm then
AForm.RemoveComponent(AControl.Controls[I]);
end;
AForm.RemoveComponent(AControl);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Form2.InsertedPanel := Panel1;
Panel1.Parent := nil;
RemoveComponents(Self, Panel1);
Form2.InsertComponent(Form2.InsertedPanel); // < this is not necessary
Form2.InsertedPanel.Parent := Form2; // as long as Parent is set
Panel1 := nil; // or if you free the panel
end; // manually
The extra reference may seem a bit silly: Form2.InsertedPanel and Panel1 point to the same object, but it's kind of semantically preferred. Maybe a central controlled variable is better.
Update:
I falsely assumed that RemoveComponent cascaded to the child controls on the panel. It doesn't, of course, so only removing the panel from form A would leave all the child controls of the panel still owned by form A. So I added the RemoveComponents routine to remove ownership from all the child controls of the panel.
Note that the child controls of the panel don't have an owner at this time. But since they are parented controls of the panel, destruction of the panel will free those controls. So be sure the panel has a parent, or free the panel explicitly.
All of the above only applies to a design time created panel, placed design time on the form, which was my assumption. Since this changing parents behaviour is apparently wanted or needed, you might want to consider to implement it completely at runtime. To keep the abbility to design the panel designtime, I suggest to create a Frame on which you can design that panel, and jump the Frame around your forms.
You can easily have something appear as if it was a panel, and also as a form, by really using a TForm for what you would have used the panel for. Then dock the form at runtime into the place where you have a blank panel left for that purpose, and undock it at runtime, by the same manner.
You can't undock a TPanel and have it appear as a top-level form window, but you can take a top level form window and dock it in code. To get the appearance and functionality you want you must use the correct tools (TForm, in this case).
Incidentally, component libraries like Toolbar 2000 do allow floating toolbar windows based on toolbar panels, so if you really insist on having all the designtim elements remain in one form, at desigtime, you should look into how it works in Toolbar 2000. It has a lot of code in there to render the toolbar in "undocked/floating" mode, and to handle the mouse-driven docking and undocking of toolbars into toolbar docks.
If the panel and child components are created at runtime, you can just set the Parent of the Panel to FormB:
Panel1.Parent := FormB;
Note that FormB has to have been created already before you can do this.
For more info, see the Delphi Wiki page here.

Duplicate ControlBox

I have inherited an application that was started as an MDI program, but the necessary background work was never put in place to fully support MDI. I'm trying to build in just enough MDI support to make the application look good and work properly as an SDI application.
Here's what I am observing, and I don't know how this is happening or how to fix it.
The MDIchild form's border is shown above the MDIparent form's menu strip.
The MDIchild form has two icons in the upper left.
The MDIchild form has two ControlBoxes in the upper right.
Any ideas why this would be?
I'd be really happy if the MDIChild window border (including the icons and control boxes) was removed entirely.
Thanks,
SH
I created the child form but showed it in normal state instead of maximized. It's as if showing the parent form in the same process as the maximized child form caused the form to be constructed out of order. My code looked something like this...
frmChild.WindowState = FormWindowState.Normal
frmParent.Show() 'frmChild.Show called within form_load of parent
frmChild.WindowState = FormWindowState.Maximized
And yes, I set the Child window's FormBorderStyle to None, but like I said, I didn't want an MDI application.
I think this must be a bug in Visual Studio. For so many people to have difficulty with it, it can't be right.
I found:
Call Show() on your MDI form before setting the WindowState property on your child form to get rid of the multiple control boxes.
Do not set the WindowState to Maximized in the designer -- do it in code after you've shown the parent.

Delphi form minimise issue

I currently have a delphi 7 project sitting in front of me and what the original creators of the software have done is used the main form as a launch pad for another form which contains the actual controls and logic behind the entire application. So basically, form1 loads up, is set to invisible and another form (the form with all the UI controls and logic) is created and shown, its a strange way to do things, but its the way they did it.
Now I'm not familiar with the way delphi 7 handles its forms, but this second window, the window with all the controls on, whenever i click the minimise button, the form does not drop down to the taskbar as one would expect, but rather, resizes so that only the minimise, maximise and close buttons are visible and then places itself at the bottom left of the screen, just above the start menu.
the creation of this second window is:
frmPlatform := TfrmPlatform.Create(frmMain);
ModalResult := frmPlatform.ShowModal;
where frmMain is the invisible form.
My question is, why does the second window not minimize as one would expect and drop to the taskbar? and how do i get it to work,
thanks
Standard behaviour of forms I'm afraid as the mainform is by default the only form shown by Delphi on the taskbar. You can however set other forms to appear on the taskbar as well:
procedure TForm1.CreateParams
(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle
or WS_EX_APPWINDOW;
end;
And you can hide your current main form of course as well, see: http://delphi.about.com/od/adptips1999/qt/hidefromtaskbar.htm, and http://delphi.about.com/od/delphitips2008/qt/hide_taskbutton.htm for D2007 and up.
I think you will find that in standard Delphi apps only the main (first) form will be shown in the task bar. Since your main form is invisible that's your issue. The minimize is probably due to the second form being called with show modal rather than simply show.
Can you remove the first form or change which form is the main form? This can be done from the project settings page.

Merge tabs from child form into main form

I have page control in main form and page controls in child form , I
place the child form inside the main form by using docking features.
I am not using MDI style
Now both forms have tabs in page control and I need to merge the child form
tabs into the main form page control, what is the best way to do that?
The simplest and best way to handle multiple tabs on a page control is usually with Frames. Take the contents of each tab and factor them out into an individual frame for each tab. Move any shared components, state and code to a data module. Then just place those frames on the tab sheets of your page control.
This is a fairly simple approach that may or may not suit your needs.
For each child tab page you need to merge:
Create a tab in the main form page
control corresponding to the child
tab
Iterate over the Controls in the
child tab and change the Parent
property to the tab page you just
created
You do not need to deal with controls that are children of other controls. e.g. if you have a groupbox in your child tab, then changing it's parent will bring both it and all controls within it to the new parent.
If you need to be able to "unmerge" your tabs at any point then you will need to devise some mechanism to keep track of where the controls came from so you can restore the original Parent as/when needed.
To make things slightly easier you could place a TPanel in the child tabs, with it's Align property set to alClient. Place all the controls in the tab on that panel and then when you need to merge/unmerge you need only set the Parent of the panel control.
I just tried
procedure TForm1.Button1Click(Sender: TObject);
begin
while Form2.PageControl1.PageCount > 0 do
Form2.PageControl1.Pages[0].PageControl := PageControl1;
end;
and it worked fine. Am I missing something obvious, or why is everybody giving such sophisticated solutions? :-)

Resources