Problem while using Two Forms - delphi

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.

Related

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)

How to get to the elements on a page control?

I'm working with Delphi 7.
Suppose I have a page control on my form. This page control has two or three tabs. Each tab has a few other controls like a label, edit etc. on it. How to get, for example, to an edit's text property in the code?
Iterate across the controls of the tabsheet using its ControlCount and Controls properties.
for i := 0 to TabSheet.ControlCount-1 do
begin
if TabSheet.Controls[i] is TEdit then
ShowMessage(TEdit(TabSheet.Controls[i]).Text);
end;
This will iterate over all immediate children of the tabsheet. If you need to iterate deeper into the children of the children and so on then you want a recursive solution.
If you want to search in each tabsheet then you need to iterate over them too.
for i := 0 to PageControl.PageCount-1 do
TabSheet := PageControl.Pages[i];
for j := 0 to TabSheet.ControlCount-1 do
begin
if TabSheet.Controls[j] is TEdit then
ShowMessage(TEdit(TabSheet.Controls[j]).Text);
end;
You can still directly access the TEdit
Edit1.Text := 'My Edit box on a Tab';

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 embed a firemonkey form inside a control?

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.

How to add a drop down menu to an Action Item at Runtime

I'm using the following method to create a an ActionClient with an Action at run time.
procedure TMainForm.AddToProjectHistory(Path: string);
var
NewOption: TAction;
ActionClient: TActionClientItem;
begin
NewOption := TAction.Create(self);
NewOption.ActionList := ActionManager1;
NewOption.Caption := Path;
NewOption.OnExecute := ProjectHistoryExecute;
ActionClient := TActionClientItem(aToolBarFile.ActionClient.Items[0].Items.Add);
ActionClient.Action := NewOption;
ActionClient.Caption := Path;
end;
This works fine if there is already an item in the list, but doesn't work at all if there isn't
e.g. if I add an Item at design time then I can add more items at runtime
But if I don't add anything at design time, theres no Drop Down to display the list of items, no drop down appears after adding items.
This doesn't have to be done with Actions but the rest of the menu system uses actions and I don't think I can add standard MenuItems to the action drop down.
Delphi 2005
The VCL automatically creates button controls of a type that depends on whether the item has child elements. By default (and depending on the ActionManager's style setting), for an ActionClientItem which has child items, a TXPStyleDropDownBtn button is created, and for a childless ActionClientItem, a TXPStyleButton is created.
So when the first child item is added during run time, the button is of the wrong type. Changing the type of that button would require destruction of the current button and a complete and manual instantiation of the new button. This should be possible, but have not tried, because:
The really most easy solution is to fool the VCL by adding a child item at design time, and to delete that item on form creation:
procedure TForm1.FormCreate(Sender: TObject);
begin
aToolBarFile.ActionClient.Items[0].Items[0].Free;
end;
if you create an actionclient of type "context", it won't have to change button type. The menu will drop down when you right click on the button instead.

Resources