Handle Needed For Firemonkey - delphi

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;

Related

Is there a way to implement a bottom-up event notification in Firemonkey?

I have a container control (TLayout or TPanel or TScrollbox, etc.) which can have several other nested controls inside. Is there a way in Firemonkey to know when the container becomes "active"? I mean for example:
when any of its children get focus or
user clicks any of its non-focus controls
I am asking for a kind of event-bubble. OnClick would be enough so when user clicks any nested control, the parent container is notified. Control's HitTest property would be a solution but if I set that property to False (to let the parent manages the onlick) on a TEdit, it becomes useless obviously (I can't type on it)
Edit:
A simple layout example:
Drop a TScrollBox in a form
Drop a TPanel in form and put it as child of the TScrollBox
Drop a TEdit in form an put it as child of TPanel
Ok, I can simply do a sort of Parent.Parent.Click after receive the OnClick on TEdit but imagine now several TScrollBoxes, with different level of nesting using TPanels with a bunch of different controls inside. It doesn't make sense neither adding the onclick code for each control in the tree, nor change the hierarchy of Parent.Parent.Click every time I redesign the layout adding an intermediate parent control.
Is there any way to do this in Firemonkey (either for Windows or MacOS)?
PS: I'm using Delphi 10.2.3 (Tokyo)

How can un-embed a subform from another form?

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;

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.

MDI interface with FireMonkey

Is it possible to create an MDI application using FireMonkey in a traditional sense of many documents forms/frames and one master form?
I'm specifically interested since there are no MDI controls on forms anymore.. Has it been replaced with something different?
EDIT: Adding to the question, when I create child forms they all showed separately in taskbar, even the OpenDialogs..
Here's one approach you can use:
1) Create your individual forms as normal, except use a TLayout, call it "LayoutMain", (aligned alClient) as the parent of all controls on that form.
2) On your "master form", when you want to bring a form instance in to behave as it would as an MDI interface, create a TLayout (call it "FormContainer" or something), place as a child of that (aligned to top) another TLayout (call it "FormHeader"), containing the individual controls for the Form Caption, the Minimize/Maximize/Close buttons and anything else you'd like on the "frame" heading. This is called a Composite.
3) Create an instance of your child form (but don't show it), then parent that instance's primary TLayout "LayoutMain" to your main form's TLayout "FormContainer".
4) On the TLayout previously mentioned (with the name "FormHeader"), give it OnMouseDown and OnMouseMove events to provide the ability to drag it around the Master Form.
When you want to maximize the child form inside the master form, you'd just set the outer TLayout "FormContainer" align property to alClient, remembering to store its original Top, Left, Width and Height values within the the form's instance so you can recall them when pressing the Restore button.
True, this is a fairly involved solution, but to the very best of my knowledge this is the only way you're going to achieve what you're trying to do with FireMonkey as it exists today (circa Update 2).
I wouldn't expect Embarcadero to provide any form of MDI emulation as part of FireMonkey "out of the box", as MDI is considered an old-fashioned approach.
The more modern solution would be to use Docking, as the RAD Studio IDE itself does. This provides the very best of both worlds, giving the user the freedom to choose what child forms they want to link into the master form, or display outside of that on their own (or any combination of docked arrangements).

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.

Resources