How embed a firemonkey form inside a control? - delphi

I have tried to embed a form inside a Scrollbox:
procedure TfrmMain.FormCreate(Sender: TObject);
var
Control:TControlView;
begin
Control := TControlView.Create(Self);
Control.BorderIcons := [];
Control.parent := ListControls;
Control.width := 800;
ListControls.AddObject(Control);
Control.Visible:= True;
end;
However the form is displayed behind tfrmMain and outside the bouns of the form.
My idea is put a form inside a panel, and both inside scrollbox. Each form represent a complex item with several controls and stuff (the reason to not use ListBox? Firemonkey control creation is far harder than simply do a form and embed it)

The secret is in how you design your child form.
You need to create a control as a container, say a TLayout (no styling), TRectangle (Basic styling) or TPanel. I'd go with the TLayout. Decide on a name for your container, say 'Container' for the sake of argument. Now create you child form and simply assign the Parent of Container to your parent object.
So, from your code above (I'm assuming TControlView is your child form):
procedure TfrmMain.FormCreate(Sender: TObject);
var
Control:TControlView;
begin
Control := TControlView.Create(Self);
Control.Container.parent := ListControls;
Control.Container.width := 800;
end;

You have to set the container control's ClipChildren property to true.

Here is a step by step instruction:
Design your embedded form. Place a TLayout with alignment alClient onto your form. Place all controls inside this layout:
TFormEmbedded = class(TForm)
LayoutMain: TLayout;
//....
end;
Design your master form.
Place a Layout onto your master form, that shall later contain the subform.
Add the following code to FormCreate of your master form:
procedure TFormMaster.FormCreate(Sender: TObject);
var
SubForm: TFormEmbedded;
begin
SubForm := TFormEmbedded.Create(Self);
SubForm.LayoutMain.Parent := Self.LayoutSubForm;
end;
Thanks to nexial for the original description.

Related

Procedure to change the color of multiple panels with different names in Delphi

I have a TForm with 5 TPanel controls on it. I need them to change color when they are clicked on.
I can easily write code for each TPanel to change colors, but that consists of too much code, so I was wondering if I could write a procedure to change the color of each one independently and just call this function with the name of each TPanel as a parameter? If not, then is there any other way to do this?
This is really easy.
Create a new VCL application.
Add five TPanel controls to the main form.
Select all five panels.
In the Object Inspector, set ParentBackground to False.
In the Object Inspector, click the Events tab and click the empty field to the right of the OnClick row caption. Type PanelClick and press Enter.
Write the following code:
procedure TForm1.PanelClick(Sender: TObject);
begin
if Sender is TPanel then
TPanel(Sender).Color := RGB(Random(255), Random(255), Random(255));
end;
I think this is what you asked. However, given your image, I suspect you rather would like a toggling behaviour:
procedure TForm1.PanelClick(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ControlCount - 1 do
if Controls[i] is TPanel then
if Controls[i] = Sender then
TPanel(Controls[i]).Color := clHighlight
else
TPanel(Controls[i]).Color := clBtnFace;
end;
This iterates over the child controls of Self, since ControlCount and Controls mean Self.ControlCount and Self.Controls, respectively. If your target panels have a different parent (a panel, say), iterate over its children instead.
Optionally, you can give the target panels a specific Tag value (like 500) and only change the colour of such panels.
And please set BevelOuter to bvNone. We have left the 90s.

How do I tile MDI children and maintain BorderStyle = bsNone?

I am hoping someone can help
I have created an object that has a form. I instantiate this object during runtime. The user can then instantiate a second (or multiple) object(s) via a menu step. So two child forms now exist in a parent form. Each objects form's BorderStyle is set to none and Style is set to MDIChild during form creation at runtime:
BorderStyle := bsNone;
FormStyle := fsMDIChild;
All fine. Form has no border.
Now I want to tile the two forms created. Menu > click Tile
procedure TMainForm.Tile1Click(Sender: TObject);
begin
Tile;
end;
Now the border re-appears. I need a way to get rid of the border.
I have tried stepping through the collection of objects and explicitly setting the BorderStyle to none:
procedure TMainForm.Tile1Click(Sender: TObject);
var
i: byte;
begin
Tile;
for i := 0 to GraphCollection.Count-1 do
(GraphCollection.Items[i] as TGraphForm).BorderStyle := bsNone;
end;
This does not work.
Does anyone have a workaround that retains the bsNone style after either Tile or Cascade.
You can do this in another way. I do this with Tabset (TTabSet class) without using fsMDIChild(but BorderStyle = bsNone).
You can add a tab for each form you open on TabSet. By clicking any tab, you can activate or show appropriate form. And delete the appropriate tab for any form by closing it. I prepare an example and you can get it from here.
(I miss-understand the question, my answer is about simulating MDITab for MdiChild forms when the FormStyle is fsNormal and not fsMdiChild)

TPanel as a splash screen in a MDI app

I want to show a TPanel in the middle of a form that is MDI parent for other forms. Some kind of 'splash' form, but not quite. The panel will contain links/buttons/shortcuts from where the user will call misc. functions.
The main requirement is that the TPanel should be placed below the MDI child form(s) when I click the MDI child. However, as it is, the TPanel will ALWAYS stay above the MDI child forms.
Calling Panel.SendToBack will make the panel disappear. How can I do?
You will need to override the Panel's WindowProc so that the panel will always be behind the MDI children e.g.:
TMainForm = class(TForm)
...
private
FPanelWndProc: TWndMethod;
procedure PanelWndProc(var M: TMessage);
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Windows.SetParent(Panel1.Handle, ClientHandle);
// Override Panel1 WindowProc
FPanelWndProc := Panel1.WindowProc;
Panel1.WindowProc := PanelWndProc;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
// Restore Panel1 WindowProc
Panel1.WindowProc := FPanelWndProc;
end;
procedure TMainForm.PanelWndProc(var M: TMessage);
var
P: ^WINDOWPOS;
begin
if M.Msg = WM_WINDOWPOSCHANGING then
begin
P := Pointer(M.LParam);
// Always place panel at bottom
P.hwndInsertAfter := HWND_BOTTOM;
end;
FPanelWndProc(M);
end;
Note: To quickly test the code, you can create a MDI application via File -> New -> MDI Application
EDIT: The code above dose infact answers your initial question. If you want your "Panel to behave somehow as a MDI child" (your comment quote), then simply (...hmmmm...) use a MDI Child form. i.e. create a new form with .FormStyle = fsMDIChild, and then use something like:
SetWindowLong(Child.Handle, GWL_STYLE,
GetWindowLong(Child.Handle, GWL_STYLE) and not (WS_BORDER or WS_DLGFRAME or WS_SIZEBOX));
To remove it's border (since simply setting .BorderStyle = bsNone does not work).
Put whatever you need on that form, and it will move above other MDI forms once you click it.
The MDI system works by having a single window that is the parent of all the MDI child windows, known as the client window. That client window is, in turn, a child of the MDI form. The VCL implementation of MDI creates the child window for you. You can gain access to its window handle through the ClientHandle.
Since the client window is a child of the main form, and parents all the MDI forms, the only solution for you is to make this panel part of the client window.
You could take control of the painting of the client window. You can do this by replacing the window proc of the client window with one of your own. You'll also need to handle button clicks etc. But that's pretty messy.
Now, perhaps you could make your panel a child of the client window. But I'm pretty sure that will screw up your MDI, which indeed you confirm to be the case.

how to copy all the TLabels parented with a TPanel on delphi to another TPanel?

I have a TPanel on a delphi form, I want to copy all the TLabels
parented with this TPanel when i press a button and put them
in other panel.
Is there a way to do that?
Thanks.
To copy the TLabel controls from one TPanel to another you can use something like this
Procedure CopyLabels(ParentControl,DestControl:TWinControl);
var
i : integer;
ALabel : TLabel;
begin
for i := 0 to ParentControl.ControlCount - 1 do
if ParentControl.Controls[i] is TLabel then
begin
ALabel:=TLabel.Create(DestControl);
ALabel.Parent :=DestControl;
ALabel.Left :=ParentControl.Controls[i].Left;
ALabel.Top :=ParentControl.Controls[i].Top;
ALabel.Width :=ParentControl.Controls[i].Width;
ALabel.Height :=ParentControl.Controls[i].Height;
ALabel.Caption:=TLabel(ParentControl.Controls[i]).Caption;
//you can add manually more properties here like font or another
end;
end;
and use like this
CopyLabels(Panel1,Panel2);
you can use the RTTI too, to copy the properties from a control to another, but as you does not specify your Delphi version only i show a simple example.
TPanel is a container of Components. It has a list of its child components in its Controls property. You may iterate over this list to get access to its children.
At the press of the button your code has to
iterate on the Controls list of Panel1
check if the control is a TLabel
change the Parent property of the TLabel to be Panel2
something like this
for i := 0 to Panel1.ControlCount - 1 do
if Panel1.Controls[i] is TLabel then
(Panel1.Controls[i] as TLabel).Parent:=Panel2;
But, wait!, this will not work. Why? Because doing this change 'on the fly' you will be changing the very same list you are iterating over.
So, you have to save the labels to be moved in a temporary list. Something like this...
var
i:integer;
l:TObjectList;
begin
l:=TObjectList.Create;
l.ownsObjects:=False;
for i := 0 to Panel1.ControlCount - 1 do
if Panel1.Controls[i] is TLabel then
l.add(Panel1.Controls[i]);
for i:= 0 to l.Count-1 do
(l[i] as TLabel).Parent:=Panel2;
l.Free;
end;

Problem while using Two Forms

i don't know how to ask this question. problem is given below.
I'm using One Main form and many sub forms but not MDI Forms.
Main form Contains 5 Buttons and a Panel. each button will call a form
inside that Panel(as Parent). in that sub forms, one form(Sub3) contain TMainMenu component.
every form is working correctly while calling by clicking the buttons but, while calling the form(Sub3) the TMainMenu is not in visible. i don't know how to bring it visible.
Please help me any one.
Thanks in Advance.
Thanks & Regards,
Yuvaraj
You can only have one MainMenu on each form. While you can have multiple forms in an app each with its own MainMenu, if you show one form within another form, only the mainmenu of the "outer" form will be visible.
When you "reparent" a form to another (show formB as a "component" on formA), then you have to merge the menu's yourself as #skamradt already mentioned.
To do so, simply have your buttons use a "SwitchToForm" function like:
type
TMain_Form
...
private
FCurrentForm: TForm;
procedure SwitchToForm(showForm: TForm);
...
end;
procedure TMain_Form.SwitchToForm(showForm: TForm);
begin
if (FCurrentForm <> nil) and (FCurrentForm.Name = showForm.Name) then begin
// Naught to do
end else begin
// If a form is currently showing, hide it and if it has a menu, unmerge that
if FCurrentForm <> nil then
begin
FCurrentForm.Hide;
if Assigned(FCurrentForm.Menu) then
begin
MainMenu.UnMerge(FCurrentForm.Menu);
end;
end;
// Set the current form to the one passed in and re-parent that to the main form
// If the form has a menu, merge that with the main menu of the main form and then
// show it.
FCurrentForm := showForm;
with FCurrentForm do begin
Parent := self;
Align := alClient;
BorderIcons := [];
BorderStyle := bsNone;
end;
if Assigned(FCurrentForm.Menu) then begin
MainMenu.Merge(FCurrentForm.Menu);
end;
FCurrentForm.Show;
end;
end;
In this example: the form is parented to the main form itself, but you could of course also parent the forms to a panel or some other container on the main form.
You can use the TMainMenu.Merge and TMainMenu.Unmerge to merge/unmerge the sub-form menu with the main form menu. In your "OnActivate" for each of the child forms, I would send a custom message to the main form that would unmerge any menus from another form (which might already not be set) and merge the menu for the current form.

Resources