Delphi: Maximize a Child Window in MDI Project - delphi

There is the good component to maximize a child window in a client area (the SHIFT key must be held) - NLDExtraMDIProps.
Or this code can be used:
procedure WMSIZE(var Msg: TMessage); message WM_SIZE;
procedure TForm2.WMSIZE(var Msg: TMessage);
begin
inherited;
if Msg.WParam = SIZE_MAXIMIZED then
begin
ShowWindow(Handle, SW_RESTORE);
Left := 0;
Top := 0;
Width := Form1.ClientWidth - 4; // The BORDER
Height := Form1.ClientHeight - 4;
end;
end;
But maximizing isn't real maximizing. The child window is only aligned to the client area. It must automatically resize and fit the client area when the parent window is resized, the maximize/restore system button must change etc.
I try to accomplish effects that are described below.
As you see on the pictures the child windows are maximized, and
they don't take the entire parent window (only the client area).
It's impossible to move them over the caption/title bar because they are maximized.
They have the restore button, not the maximize button any more.
They are aligned to the client area (resizing of the parent window causes resizing of the child one withing the client area).
The code in my question and the component don't do like the child windows on the pictures.
Can we make a window really maximized (not just aligned)?
Not maximized (not good; the component and the code from my question maximize like on these pictures):
Maximized (what I need):

I do not understand your problem. Maximizing an MDI child window is done:
programmatically: by using ShowWindow(ActiveMDIChild.Handle, SW_MAXIMIZE),
manually: by clicking the Maximize border icon, or by double clicking on the form caption.
Both these actions result in:
the disappearance of the child window border (collapses into the MDI form border),
the addition of small border icons (for the child window) to the main menu bar,
a resize effect similar to that of Align=alClient.
To restrict the available space for the child windows within the main form, make sure to align windowed controls to edges of the form.
Setting the Align or Anchors properties for MDI child windows has no effect: they are not part of the default VCL aligning implementation anymore; Windows has taken over that job.
If you want to intervene on the resizing of an MDI child, then handling WM_SIZE is the wrong approach, because that message is send áfter the resize. Instead, handle WM_SYSCOMMAND as I explained here.
As for my component that you refer to:
Manually maximizing by clicking the Maximize border icon does exactly thát: a default maximize operation as outlined above,
Manually maximizing by clicking the Maximize border icon - while holding the Shift key - does resize the child window to the largest spare space within the MDI form. In this case, resizing the MDI main form does nót resize the MDI child forms.

if (Msg.WParam = SIZE_MAXIMIZED) then
begin
Left := 0;
Top := 0;
Width := frmMain.ClientWidth - 4;
Height := frmMain.ClientHeight - 4;
SendMessage(Handle, WM_SIZE, SIZE_RESTORED, 0);
end;

Related

How do I make two controls each occupy half their parent's area?

I have an application that has a sidebar attached to it (TPanel --> alRight), that uses a CategoryPanel (alClient) inside of it. This CategoryPanel has exactly 2 Groups which are non aligned. I would like to share the boundary of these 2 groups so it would fill up the whole panel space in a 50/50 ratio. Unfortunately The CategoryGroups do not support alignment in designtime, which forces me to run my application every time I want to test it. I tried to set each CategoryGroup to the height to half of the panel, but it displays scrollbars. (See Picture 2)
How can I align/share the boundary in a ratio of 50/50 properly?
According to your comments, you want to run this code:
procedure TForm1.UpdateGroupHeights;
begin
if not CategoryPanel1.Collapsed then
CategoryPanel1.Height := CategoryPanelGroup1.ClientHeight div 2;
if not CategoryPanel2.Collapsed then
CategoryPanel2.Height := CategoryPanelGroup1.ClientHeight -
CategoryPanelGroup1.ClientHeight div 2;
end;
whenever anything changes that you wish to affect the layout of your groups. So I think you need to call this function from the following events:
The OnCreate event of the form.
The OnResize event of the TCategoryPanelGroup.
The OnCollapse and OnExpand events of the two category panels.
That looks a bit weird though when one panel is collapsed, and the other is expanded. Personally I'd rejig the code to fill all available space.
if not CategoryPanel1.Collapsed then
;//nothing to do
if CategoryPanel1.Collapsed and not CategoryPanel2.Collapsed then
CategoryPanel2.Height := CategoryPanelGroup1.ClientHeight-CategoryPanel1.Height;
if not CategoryPanel1.Collapsed and CategoryPanel2.Collapsed then
CategoryPanel1.Height := CategoryPanelGroup1.ClientHeight-CategoryPanel2.Height;
if not CategoryPanel1.Collapsed and not CategoryPanel2.Collapsed then
begin
CategoryPanel1.Height := CategoryPanelGroup1.ClientHeight div 2;
CategoryPanel2.Height := CategoryPanelGroup1.ClientHeight-CategoryPanel1.Height;
end;

How can I create a Delphi child window that has a main menu?

I am working with a Delphi form that contains a TMainMenu. In a particular situation I want to show this form parented and client-aligned inside another form. This works fine but the main menu of the parented form does not appear. I see a comment in this SO question that states "A child window cannot have a menu". Is there anything that I can do to override this behaviour and make a TMainMenu appear?
An aside: I've only just noticed this because where I have used this principle before, I've been using the Developer Express menu component and this is quite happy to show in a child form.
Later edit:
Using the code from TLama below, this works (but the child menu is not themed, I,e very plain):
This works:
procedure TForm65.FormShow(Sender: TObject);
begin
Winapi.Windows.SetParent(ChildForm.Handle, Handle); // <<<<<<<<
ChildForm.BorderStyle := bsNone;
ChildForm.Align := alClient;
ChildForm.Show;
end;
This code DOES NOT work. Why?
procedure TForm65.FormShow(Sender: TObject);
begin
ChildForm.Parent := Self; // <<<<<<<<<
ChildForm.BorderStyle := bsNone;
ChildForm.Align := alClient;
ChildForm.Show;
end;
MSDN makes this perfectly clear:
A child window has a client area but no other features, unless they are explicitly requested. An application can request a title bar, a window menu, minimize and maximize buttons, a border, and scroll bars for a child window, but a child window cannot have a menu.
This refers to the menu as drawn by Windows itself. If your component custom draws a menu bar, then of course it can have a menu, even if it is a child window.
Your call to SetParent does not make your window into a child window. This is explained in the documentation:
For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed. Therefore, if hWndNewParent is NULL, you should also clear the WS_CHILD bit and set the WS_ POPUP style after calling SetParent. Conversely, if hWndNewParent is not NULL and the window was previously a child of the desktop, you should clear the WS_POPUP style and set the WS_CHILD style before calling SetParent.

Why doesn't my run-time-created component appear on the form?

I am testing the example that came from this Q&A Component Creation - Joining Components Together? to learn how to create a custom/composite component.
While the installed component from the example works dragging on to the form, I can't seem to create it at run time.
procedure TForm1.Button1Click(Sender: TObject);
var
MyPanel2 : TMyPanel;
begin
MyPanel2 := TMyPanel.Create(Form1);
With MyPanel2 do
begin
Left := 10;
Top := 10;
Width := 400;
Height := 400;
Visible := True;
Image.Picture.LoadFromFile('C:\test.png');
end;
end;
I tried both self and Form1 as the owner. Played with properties of both the panel and the image.
Just not sure what I am doing wrong. No errors except when I forgot to add pngimage to my uses. Steps through the code just fine, nothing visually occurs for the run time creation.
You need to set Parent in the runtime code.
MyPanel2 := TMyPanel.Create(Self);
with MyPanel2 do
begin
Parent := Self;//oops, you forgot to set this
SetBounds(10, 10, 400, 400);
Image.Picture.LoadFromFile('C:\test.png');
end;
The code in your question won't result in the control showing for a plain vanilla TPanel, or indeed any control.
From the documentation, with my emphasis:
Specifies the parent of the control.
Use the Parent property to get or set the parent of the control. The
parent of a control is the control that contains it. For example, if
an application includes three radio buttons in a group box, the group
box is the parent of the three radio buttons, and the radio buttons
are the child controls of the group box.
To serve as a parent, a control must be an instance of a TWinControl
descendant.
When creating a new control at run time, assign a Parent property
value for the new control. Usually, this is a form, panel, group box,
or a control that is designed to contain another. Changing the parent
of a control moves the control onscreen so that it is displayed within
the new parent. When the parent control moves, the child moves with
the parent.

detect if the scrollbars of a form are visible in an mdi child

I cannot detect if the scrollbars of a form are visible.
Googleing the Internet shows that the code below should work. Everybody uses it:
function VertScrollBarVisible(WindowHandle: THandle): Boolean;
begin
Result:= (GetWindowlong(WindowHandle, GWL_STYLE) AND WS_VSCROLL) <> 0
end;
I call it like this:
procedure TFrmBaser.Button1Click(Sender: TObject);
begin
if VertScrollBarVisible(MainForm.Handle)
then Caption:= 'visible';
end;
It returns False all the time, even is the scrollbars are visible. They are made visible by some MDI child forms that I drag a bit out of screen.
Delphi 7, Win XP SP3, Themes on
This
It returns False all the time, even is the scrollbars are visible. They are made visible by some MDI child forms that I drag a bit out of screen.
shows that the form you are having problems with is a MDI parent form (FormStyle is fsMDIForm).
MDI parent forms are different from normal forms in that they create a special client window that fills the whole client area of the form, and which manages the MDI child windows / forms. The MDI client window will never be larger than the client area of its parent, so the parent form will never show scrollbars. This explains that the code in your question always returns false.
The scrollbars you see are part of the MDI client window. Modify your code to check the window style of the client window, its handle can be accessed with the ClientHandle property of the MDI parent form:
procedure TFrmBaser.Button1Click(Sender: TObject);
begin
if VertScrollBarVisible(MainForm.ClientHandle) then
Caption := 'visible';
end;
For more information about MDI at the Windows API level see About the Multiple Document Interface on MSDN.
Try this:
function VertScrollBarVisible(Form : TForm) : Boolean;
begin
Result:=(Form.Width-Form.ClientWidth>10)
end;
I'm not sure if it works, but it compares the "available" width of the form against the "total" width of the form (normally they are within 2-3 pixels of each other, but with a scroll bar, the available width should be significantly lower).

Image Preview in a Listbox

How can I display a preview (almost like a hint) of an image when I hover the mouse over an item in a listbox of filenames? I've tried showing a form and loading the image, but when the preview form shows, I lose focus for the listbox which means that when I move the mouse, the preview image does not change when I go to the next item in the list.
Thanks, Pieter.
I have, based on the answer from TOndrej, tried to implement a custom THintWindow, but the Canvas.StretchDraw does not draw the bitmap sent as a parameter. Any ideas why not? Text is displayed normally.
procedure TFormMain.DisplayPreview(HintImage: TBitmap);
var
CustomHint: THintWindow;
Rect: TRect;
MousePoint: TPoint;
begin
*{
Based on Source: http://www.chami.com/tips/delphi/112996D.html
}*
GetCursorPos(MousePoint);
with Rect do
begin
// set the position and size of the hint window
Left := MousePoint.X;
Top := MousePoint.Y;
Right := Left + 50;
Bottom := Top + 25;
end;
CustomHint := THintWindow.Create(Self);
try
with CustomHint do
begin
// set the background color
//Color := clNone;
**Canvas.StretchDraw(Rect, HintImage);**
ActivateHint(Rect, 'Hint');
Application.ProcessMessages;
//
// perform your tasks here
// before closing the hint window
//
Sleep(500);
ReleaseHandle;
end;
finally
if Assigned(CustomHint) then
CustomHint.Free;
end;
end;
To me it seems you want a custom hint window. To do this you should write a new THintWindow descendant and either set it globally to the whole application by assigning your new class to the HintWindowClass global variable in Forms unit, or write your own listbox descendant in which you will handle CM_HINTSHOW message and assign your new hint window class to HintInfo.HintWindowClass. (HintInfo points to a record passed to your control in the CM_HINTSHOW message by the VCL.)
1) Are you displaying your preview form like a dialog (Modal Window) if yes then change it to non modal window.
2) Remember to set focus back to your parent window once the preview form shows up, that way your parent form that has the listbox has the focus and it will pass the mouse move events to the listbox.
Best Regards.

Resources