Make 2 forms able to overlap each other? - delphi

I would like to have a seperate form that shows "along" with my main form, so it does not overlap the main form.
Here's an example:
Notice how the main program, overlaps the log? I can't figure out how to do that in Delphi.
Thanks!

The answers to this question lie in the very useful Window Features MSDN topic.
The pertinent information is:
An overlapped or pop-up window can be
owned by another overlapped or pop-up
window. Being owned places several
constraints on a window.
An owned window is always above its owner in the z-order.
The system automatically destroys an owned window when its owner is
destroyed.
An owned window is hidden when its owner is minimized.
The main form in your app is the owner (in Windows terminology rather than Delphi terminology) of the other popup windows. The first bullet point above implies that the owned windows always appear above the main form (the owner).
Try creating an app with 3 forms and show them all. The .dpr would look like this:
program OwnedWindows;
uses
Forms,
Main in 'Main.pas' {MainForm},
Popup1 in 'Popup1.pas' {PopupForm1},
Popup2 in 'Popup2.pas' {PopupForm2};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainForm, Main);
Application.CreateForm(TPopupForm1, PopupForm1);
Application.CreateForm(TPopupForm2, PopupForm2);
PopupForm1.Show;
PopupForm2.Show;
Application.Run;
end.
You will see that the main form is always underneath the other two forms, but these other owned forms can be above or below each other. When you minimize the main form they all disappear.
You could if you want make all of your forms top-level unowned windows:
procedure TPopupForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.WndParent := 0;
end;
And like wise for TPopupForm2 in my example. This would result in all 3 windows having taskbar buttons.
One other approach is to revert to the pre-Vista way of things and make the Application's hidden window be the top-level owner window. You do this by making sure that Application.MainFormOnTaskbar is False. Skip all the CreateParams code and you'll now have a single window on the taskbar and any of your windows can be above any other because the top-level owner window is the hidden window Application.Handle. Of course the downside is that you lose your Aero Peek.
So, I guess what you need to do is to make the main form appear on the taskbar as usual, but ensure that the other forms are not owned (in the Windows sense) by the main form. But they need to be owned to avoid having them in the taskbar. So you can make the hidden application window be the owner using the CreateParams method, like so:
procedure TOverlappedPopupForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.WndParent := Application.Handle;
end;
Although you state otherwise in the comments, when I do this I find that the popup form is indeed hidden when I minimize the main form. And it is shown again when the main form is restored. Thus I think this does solve your problem completely.

I haven't got Delphi open now, but would setting
mainform.formstyle := fsStayOnTop
and show the child form with
childform.show;
work?
or else try using SetWindowPos() and setting the hWndInsertAfter property to something like HWND_TOPMOST on the main form

Related

Delphi destroy dynamic control onDestroy of the parent form

How do I make sure a dynamic control is destroyed along with it's parent form?
So, from the main form, I create a button for a secondary form, then I display the secondary form with the button on it.
Now, I want to make sure the created button is destroyed together with the secondary form.
Would it be enough to make the Parent of the Button, the secondary form? Would that do it?
I am using a custom descendant of the TButton Class - TMyButton. So in my constructor I have the next code:
constructor TMyButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Self.OnClick := Self.MyButtonClick;
Self.Parent:=TWinControl(AOwner);
self.Visible := true;
end;
Is this going to be ok?
It works for me, it throws no errors, but I want to make sure that the said button is destroyed together with the form on which it is placed.
The MyButton will be placed on a secondary form, let's say 'Form2'
So there will be a code like:
var
bt:TMyButton;
begin
bt:=TMyButton.Create(Form2);
bt.Parent:=Form2;
...
form2.Show;
end;
First of all: setting a control's parent in its constructor is wrong! For a control created in design time, the IDE is and will be responsible for setting a parent. For a control created in runtime, the creating code should be responsible for setting a parent.
How do I make sure a dynamic control is destroyed along with it's parent form?
A control which has a Parent will automatically be destroyed when that immediate Parent is destroyed. Furthermore, a control which has an Owner will automatically be destroyed when that Owner is destroyed. It the Owner is in the Parent chain of the control then everything is fine. If the Owner is not in the Parent chain of the control, then you have a design problem which could result in unwanted destruction.
Your current constructor is completely unnecessary. Casting the Owner (which could be nil!) to a TWinControl without checks is wrong. But as said, setting the Parent should not be there. Visible is true by default, and assigning the OnClick event will prevent it for further use. Instead, override Click.
Regarding your comment, the calling code would become:
procedure TForm1.Button1Click(Sender: TObject);
var
Button: TButton;
begin
Button := TButton.Create(Form2);
Button.OnClick := Form2.ButtonClick;
Button.SetBounds(10, 10, 75, 25);
Button.Parent := Form2;
end;
But this leaves you with no reference to the button. Instead add the variable to a higher scope.
With this code you should not have problems.
You're making both the Owner and the Parent is the child form. So when released form. This frees the objects it contains.
I will explain the difference between Parent and Owner:
When assigning the Parent, you are indicating "visual container". Is the object that show ... a Form a Panel, a Frame.
Moreover, the Owner is the owner. The Object owner is responsibility to release all objects "slaves".
Normally both the Parent and the Owner is the same, because we design interfaces automatically and put the components visually.
When you want to create visual components at runtime must be controlled well who is the Parent, and whom the Owner.
Different is if your in the Owner will set Nil:
Obj := TObject.Create(nil);
There if you have to indicate when you should release it. It's your responsibility.
EDIT:
You can use the TButton. Something like this:
procedure TForm1.CreateButton;
var obj: TButton;
begin
obj := TButton.Create(Form2);
obj.Parent := Form2;
Obj.OnClick := MyEventOnClick;
end;
EDIT 2:
Zarko Gajic, in About.com has an excellent article is worth reading.
This can help supplement what you have offered. You can view the article in http://delphi.about.com/od/objectpascalide/a/owner_parent.htm
The short answer is: Yes, your button will be destroyed along with the form.
But it would be useful if you know how to verify this for yourself.
Write the following code:
(NOTE: Don't forget override where you declare the method.)
destructor TMyButton.Destroy;
begin
inherited Destroy;
end;
Now put a breakpoint on the line inherited Destroy;. Run your application in the debugger, and ensure the code stops at the breakpoint. I.e.
Open your child form.
Destroy your child form. (Emphasis on Destroy, because if CloseAction is caHide, simply closing your form won't be sufficient for the test.)
Verify that you reach the breakpoint.
Press F9 to continue running.
Also verify that you reach the breakpoint only once per button. (Otherwise there would be a double-destroy bug that could lead to access violations and unpredictable behaviour.)
You can also enable Debug DCU's in your compiler options. Then when you hit the breakpoint examine the call stack Ctrl+Alt+S to see where in VCL code your button is being destroyed from.

How can I host a FireMonkey form client aligned inside another?

My Delphi XE7 FireMonkey project is growing controls and naturally I've moved to using frames. Where I've used frames in the VCL there have been situations where I've simply chosen to host one (complex) VCL form inside another instead, creating and displaying it in the form's OnShow and setting it client-aligned (the benefit of this is that you don't get issues with dangling inherited controls when you edit the frame).
With FireMonkey though, things have changed slightly and my attempt to get a child form client aligned inside another is stumbling. I came across this very useful SO link which shows how to host a FireMonkey form inside a VCL form so I built on this with my code as follows:
procedure THostForm.FormCreate(Sender: TObject);
begin
FForm := TChildForm.Create( Self );
FForm.BorderIcons := [];
FForm.BorderStyle := TFmxFormBorderStyle.None;
FForm.Visible := True;
FForm.Parent := Self;
ResizeForm;
end;
procedure THostForm.FormResize(Sender: TObject);
begin
inherited;
ResizeForm;
end;
procedure THostForm.ResizeForm;
begin
if Assigned(FForm) then
FForm.SetBounds( Round(ClientRect.Left), Round(ClientRect.Top), Round(ClientWidth), Round(ClientHeight));
end;
This produces a child form which changes size with the host form, but remains at the top left of the screen. I've tried various position options in the ResizeForm routine too. It seems to me that a form might not be able to be the parent of another because TForm is not IAligneableControl whereas TFrame is. So, I tried 'docking' my child form to a TRectangle client aligned in the host form and this behaves the same way.
Has anyone examined this?
* SOLUTION DETAIL AS SUGGESTED BY MARCO BELOW *
Marco's solution is very neat and reduces the 'hosting' to just two lines of code. You do need to ensure that your child (hosted) form has everything inside another client aligned control - Marco suggested using a TLayout, but I already had a TPanel that I am using for a background so I had no modifications to the child form at all. So, to host this child form TChildForm inside a THostForm simply do:
procedure THostForm.FormCreate(Sender: TObject);
begin
FForm := TChildForm.Create( Self );
FForm.Panel1.Parent := Self;
end;
Job done. Thanks Marco.
Mixing forms and controls in FireMonkey is not such a good idea as it is the VCL, because in the VCL controls and form are all TWinControl descendant with their own Windows handle, while in FireMoneky the form is associuated with an operating system object while the controls are not.
The address scenario, I've used a different solution. Created a form with a client-aligned, useless TLayout with all of the controls inside it. At runtime, create this form and parent the Layout to the new container (for example a tab in a multi tab control).
I've used this a few times, never found big issues with it, and a nice way to dynamically crate tab pages keeping the visual development model.

How to make my Forms always to be on top my main form?

How to make my non-modal forms to always be on top of my main form?
I have tried:
procedure TForm3.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WndParent := Application.MainForm.Handle;
end;
Which seems to work fine. Is that correct way?
This is the Win32 concept of window ownership. An owned window always appears on top of its owner. The owner is specified in the call to CreateWindow and can then not be modified.
In the VCL you specify the owner by setting WndParent in CreateParams, and the framework then passes that on to CreateWindow. The VCL does this for you but in older versions the owner handling is, well, somewhat flaky. Modern versions are better and allow more control through the PopupMode and PopupParent properties.
Your code will therefore have the effect that you desire.

how to create MDI form in delphi

How can I solve this problem "Cannot Create Forms. No MDI forms are currently active". I want to make a simple program that wod require a Login Form before it can acess the main form. I got three forms: Main Form (MDI Form), Form2 (MDIChild) and Login Form (Normal). Login form would appear first then the Main Form, then when I try to call on Form 2 from the Main form, an error would display "Cannot Create Forms. No MDI forms are currently active".
I am using Delphi 7 on windows XP. I'm a beginner. Thank you very much sir.
It sounds like you're letting your LoginForm be auto-created, and it's being created first. This won't work, because the first form created by Application.CreateForm in the project file becomes the Application.MainForm. In order by be an MDI application, the MainForm must be a MDI parent window.
The solution is usually to not auto-create your login form, and instead create it yourself. To do so, you need to edit your .dpr file (Project->View Source from the IDE's main menu).
Your project source should look something like this now (obviously, using your classes in the Application.CreateForm calls):
begin
Application.Initialize;
Application.CreateForm(TLoginForm, LoginForm);
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TChildForm, ChildForm);
Application.Run;
end.
You need to modify it so that the LoginForm isn't created first.
var
LoginOK: Boolean = False;
begin
LoginForm := TLoginForm.Create(nil);
try
// Show login form. When it closes, see if login worked.
LoginForm.ShowModal;
LoginOK := LoginForm.CanLogin; // However you know login is OK or not here
finally
LoginForm.Free;
end;
if not LoginOK then
Halt; // Login failed - terminate application.
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
{
I normally do not auto-create anything but the main form
and maybe a datamodule (which you **can** autocreate first -
it is not a form), but a MDI application is pretty useless
without at least one open child window, IMO.
}
Application.CreateForm(TChildForm, ChildForm);
Application.Run;
end.

Problem setting parented to new form in DLL

Please explain the difference between:
ChildForm := TForm.CreateParented(AOwner)
ChildForm := TForm.CreateParentedControl(AOwner)
ChildForm := TForm.Create(AOwner);
ChildForm.ParentWindow := AOwner.Handle
This example may be complicated and convoluted, I'd really just like an overview of when people use the different kinds of Create methods for forms.
Delphi 7 help tells me that I should use CreateParented(AOwner.Handle) and ParentWindow := AOwner.handle with non-VCL controls or across DLL's. Until yesterday I just set Parent := AOwner, and I have absolutely no idea why this stopped working.
(Maybe I just need to reboot my computer)
We have Components. They are visible or invisible items on a form or a datamodule. Each component can have an owner that is responsible for the eventual destruction. If there is no owner, you must take care of the destruction yourself.
We have Controls, which are components that are visible. They also have a Parent which contains the control. For example a Panel is the parent of a button on that panel.
We also have WinControls which are controls that link to windows objects. They also have a handle of the parent window.
So:
TMyControl.CreateParented
constructor CreateParented(ParentWindow: HWnd);
This is used to create a control from which the parent window is provided by an handle.
It creates the control without owner and sets the parentwindow to ParentWindow.
TMyControl.CreateParentedControl
class function CreateParentedControl(ParentWindow: HWND): TWinControl;
Creates the control, without owner, sets the parentwindow to ParentWindow and returns
it.
TMyControl.Create(AOwner: TComponent)
Creates a control with owner set to AOwner.
TMyControl.ParentWindow := AOwner.Handle;
Sets the parentwindow (handle) to the handle of AOwner.

Resources