How can un-embed a subform from another form? - delphi

I'd like to swap the subforms that appear in a container on my main form.
I've found a method of assigning one form as a subform of another here by making it's parent a container on the main form.
procedure TParentForm.EmbeddForm(AParent:TControl; AForm:TCustomForm);
begin
while AForm.ChildrenCount>0 do
AForm.Children[0].Parent:=AParent;
end;
This works pretty much as I expected it to for adding a subform; however, I can't seem to replace it once it's already been made a subform.
niling the parent for the subform doesn't seem to do the trick, nor does setting its parent to it self.
Is there a way to un-embed this subform from its container?
I'd rather not destroy an recreate these subforms all the time, just swap them out.

In Firemonkey, to embed a form, you should first encapsulate all your controls on a TLayout of some sort. Then, you can assign the parent of that layout to whatever container you need.
MyLayout.Parent := MyContainerInAnotherForm;
This way, you only have one variable (of the layout) to reference the entire "form". Bear in mind that FMX is widely based around layouts.
When you want to re-assign the original form, just assign that layout's parent back to its original container.
MyLayout.Parent := MyOriginalContainerForm;

Related

How do you add an item to a TStackPanel at runtime

I have a TStackPanel which I want to add a number of frames into at runtime. The number may vary each time the form is opened.
There seems to be limited information about TStackPanel around, and the examples I can find are in languages other than Delphi.
I have a loop that creates the frame, gives it a unique name and then adds it to the TStackPanel:
for i := 0 to 10 do
begin
mfSubFrame[i] := TMyFrame.Create(Application);
mfSubFrame.Name := name_array[i];
StackPanel1.InsertComponent(mfSubFrame[i]);
end;
This does not put anything in the stack panel. If I change the SP line to:
StackPanel1.InsertControl(mfSubFrame[i]);
then I do get a frame in the SP. It is the last one of the loop as I can tell by the name, the others may be hidden behind it but I can't tell. They are certainly not stacked horizontally like they should.
I have tried various other things like setting the parent of the frames to be the SP, and had a look at things like:
StackPanel1.Components.InsertComponent(mfSubFrame[i]);
and other sub-methods, but had no luck so far.
I suspect it may require a combination of statements, like add a control item as well as the actual component, but as I am working on the basis of trial and error it could be a long time before I stumble on the right combination.
I have never used the TStackPanel before, but it seems like you can add controls to it exactly the same way you add controls to any other windowed control: just create the control and assign its Parent.
For example,
for var i := 1 to 10 do
begin
var Memo := TMemo.Create(Self);
Memo.Parent := StackPanel1;
end;
will add ten memo controls (all owned by Self) to StackPanel1. There is no need to name the controls; referring to components by string name at runtime is an antipattern. (So is using FindComponent.)
InsertComponent() changes a component's Owner, which has no effect on visual display. You are creating each frame with Application as its Owner, and then changing its Owner to StackPanel1. You should assign the desired Owner when calling the component's constructor.
InsertControl() changes a control's Parent, which does affect visual display. You are creating each frame without a Parent, and then changing its Parent to be StackPanel1. You should be using the actual Parent property, not calling InsertControl() directly.
That being said, TStackPanel has a ControlCollection property that you are not doing anything with. That collection manages the actual stacking.
If needed 1, for each frame, try calling StackPanel1.ControlCollection.Add(), and then assigning the frame to the TStackPanelCollectionItem.Control property.
1: I don't have the source code for TStackPanel to look at, but I suspect TStackPanel probably handles this automatically for UI controls dropped onto it at design-time, but you might need to perform it manually for controls that you create dynamically at runtime. I'm not sure.

Delphi - move control to the ancestor form

I have more than 50 forms which have the same button on them. All of them are derived from the same ancestor. Is there any automatically way to move that button(or any other control) to the common ancestor?
David Miro had the right answer, but I think he misunderstood what you want to do. You are not trying to move the position of the butttons.
If you have not edited the buttons on the child form(s), you can add a new button on the parent. It will appear automatically on each child form. It will be a new button, which must have a different name but there will be an inherited button on each form. Then you will want to edit each child form to remove the original button. You will have a button and it will be inherited. If the event handler is always the same, you can code that into the parent as well.
If you have edited the buttons on the child forms previously, you can do this. The only way I know is to edit the DFM file of the form. A button declared in the form is defined without any reference to a parent. An inherited button is defined with an INHERIT in front of it. You need to add the INHERIT word, which tells the form that the button is inherited. If that sounds complicated, just create 2 buttons and look at the difference. It's not really too complicated.
The difficulty is this: you can't inherit from something before you create it (the parent button). And, you may have some difficulty creating the parent because the children already have a component with that name. You can change the name if you have to. But this can be done. I think this is what you are looking for. Strange there's no easier way to do this, because improvements like this are often created in the child form first.
At design time no problem. If you move parent button position , automatically moves the children buttons. But if you moved the child button, then this no longer works.
A solution. Although tedious, is to edit the form dfm child file and remove the attributes you need to inherit from dfm parent file (button.left, button.right, etc. ..)
With this procedure get it working again

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;

Delphi - child forms have frame of parent

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.

form main + form child stacking issue

I am using main form and child forms.
If I open 2nd child form with
TMYForm.Create(nil);
It does not stack it on the 1st child form top.
Is there any way for 2nd and X'th child form to appear in exact same position as 1st child form?
Or I need to destroy old child form while creating new one (as stacking is done automatically)?
That is the expected behavior. Without specifying otherwise, Forms will open on the right and down from the position of the previously opened.
If you want to control the position of your form, change its Position property to poDesigned, but you have to set its Top and Left properties to ensure it'll be visible.
Or you can used some presets: poDesktopCenter, poMainFormCenter, poOwnerFormCenter or poScreenCenter.
Try putting this:
TMYForm.Position:=poMainFormCenter;
or
TMYForm.Position:=poOwnerFormCenter;
Before TMYForm.Create(nil);
-S
If you have references to the child forms, use Form2.BoundsRect := Form1.BoundsRect

Resources