Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

MDI form special maximization

340 views
Skip to first unread message

Joe Cocker

unread,
Oct 3, 2003, 11:56:20 PM10/3/03
to
Hi,

In an MDI application, I want to prevent the normal
maximize form behavior of child forms, and instead just
stretch them over the entire workable area, while
leaving their borders visible and touching the main
form's borders.

Then, the "maximize" button should be replaced by the
"restore" button.

I tried to catch the WM_SYSCOMMAND message, but it
messes up because the form still thinks that it isn't
maximized, so that subsequent clicks on the "maximize"
button don't restore it...

Any idea how I can achieve that?

Thanks,

- Joe


Peter Below (TeamB)

unread,
Oct 4, 2003, 8:21:38 AM10/4/03
to
In article <3f7e...@newsgroups.borland.com>, Joe Cocker wrote:
> In an MDI application, I want to prevent the normal
> maximize form behavior of child forms, and instead just
> stretch them over the entire workable area, while
> leaving their borders visible and touching the main
> form's borders.
>
> Then, the "maximize" button should be replaced by the
> "restore" button.

That is the tricky part. I don't think you can do that with MDI
children. The Windows MDI subsystem jumps through quite a number of
hoops to get the normal maximize behaviour of MDI children and i see
no easy way to change that. The usual way to modify this for non-MDI
windows (handling WM_GETMINMAXINFO or WM_WINDOWPOSCHANGING) simply
don't work for MDI children.


--
Peter Below (TeamB)
Use the newsgroup archives :
http://www.mers.com/searchsite.html
http://www.tamaracka.com/search.htm
http://groups.google.com
http://www.prolix.be


Joe Cocker

unread,
Oct 4, 2003, 10:39:00 AM10/4/03
to
Fair enough. Then let's say I give up on the restore
functionality. What's the safest way to detect the MDI
parent window's workable area, assuming it includes a
TMainMenu and TStatusBar components?

This command produces a scroll bar on the main form:

ChildForm.SetBounds (
0, // Top
0, // Left
MainForm.ClientWidth, // Width
MainForm.ClientHeight - // Height
GetSystemMetrics (SM_CYMENU) - // MainMenu height (?)
MainForm.StatusBar.Height // StatusBar height
);


"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.0000a4c...@nomail.please...

Peter Below (TeamB)

unread,
Oct 4, 2003, 4:40:25 PM10/4/03
to
In article <3f7e...@newsgroups.borland.com>, Joe Cocker wrote:
> Fair enough. Then let's say I give up on the restore
> functionality. What's the safest way to detect the MDI
> parent window's workable area, assuming it includes a
> TMainMenu and TStatusBar components?

That is fairly easy. The parent for the MDI children is the MDI
clientwindow, which is a pure API window without a matching VCL
control. Its handle can be had via the main forms ClientHandle
property, its client area via

GetClientRect( ClientHandle, aTRect );

Joe Cocker

unread,
Oct 4, 2003, 10:38:17 PM10/4/03
to
Excellent! Thank you so much for your sharp replies Peter!

One last final touch though. If the MDI child is outside
the MDI client area, the MDI client will show scroll bars.
This fools GetClientRect() into excluding the scroll bars
area from the returned TRect.

This results in a strange behavior in my application, as
the child window resizes to almost the entire client area,
excluding the area where the scroll bars used to be.

Is there an elegant way to avoid this, except for executing
GetClientRect() twice?


"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.0000a4c...@nomail.please...

Peter Below (TeamB)

unread,
Oct 5, 2003, 6:03:11 AM10/5/03
to
In article <3f7f...@newsgroups.borland.com>, Joe Cocker wrote:
> One last final touch though. If the MDI child is outside
> the MDI client area, the MDI client will show scroll bars.
> This fools GetClientRect() into excluding the scroll bars
> area from the returned TRect.
>
> This results in a strange behavior in my application, as
> the child window resizes to almost the entire client area,
> excluding the area where the scroll bars used to be.
>
> Is there an elegant way to avoid this, except for executing
> GetClientRect() twice?

Well, there are two approaches. The first would be to modify your code
to check for the presence of scrollbars on the MDI client and correct
the result from GetClientRect accordingly. For that you use

if (GetWindowLong( clienthandle, GWL_STYLE ) and WS_VSCROLL) <> 0 then
...has vertical scrollbar

and the analog with WS_HSCROLL for the horizontal scrollbar. The
scrollbar width and height, respectively, can be had from
GetSystemMetrics( SM_CXVSCROLL ) and GetSystemMetrics( SM_CYHSCROLL ).

The second approach is to block the client window from sprouting
scrollbars in the first place. This may not be a suitable approach if
you want your users to be able to handle MDI children larger than the
client window. It is also a bit hackish <g>.

<quote
source="http://codecentral.borland.com/codecentral/ccweb.exe/listing?id
=19374">
> How can I hide scrollbars on a MDI Form ?
> I tried to set the properties AutoScroll, HorzScrollBar.Visible,
> VertScrollBar.visible to false but it had no effect.

this has no effect since the scrollbars do not belong to the MDI frame
window itself, they belong to the client window, which is not a Delphi
form. Which means one has to attack the problem on the API level.
Since this question has come up so frequently in recent days i have
modified a sample based on the stock MDI project to include this
feature. The salient parts are quoted below.

Open the main forms unit in the IDE.
If you don't have a handler for the OnCreate event, add one. In the
handler you do this:

If ClientHandle <> 0 Then Begin
If GetWindowLong( ClientHandle, GWL_USERDATA ) <> 0 Then
Exit; // cannot subclass client window, userdata already in use
SetWindowLong( ClientHandle, GWL_USERDATA,
SetWindowLong( ClientHandle, GWL_WNDPROC,
integer( @ClientWindowProc)));
End;

Add a new standalone function to the unit, it has to go above the
FormCreate method since it is referenced in the statement above.

Function ClientWindowProc( wnd: HWND; msg: Cardinal; wparam,
lparam: Integer ): Integer; stdcall;
Var
f: Pointer;
Begin
f:= Pointer( GetWindowLong( wnd, GWL_USERDATA ));
Case msg of
WM_NCCALCSIZE: Begin
If ( GetWindowLong( wnd, GWL_STYLE ) and
(WS_HSCROLL or WS_VSCROLL)) <> 0
Then
SetWindowLong( wnd, GWL_STYLE,
GetWindowLong( wnd, GWL_STYLE )
and not (WS_HSCROLL or WS_VSCROLL));
End;
End;
Result := CallWindowProc( f, wnd, msg, wparam, lparam );
End;

I clipped this code from a larger project, so lets hope i did not
create errors in the process. What this code does is to subclass the
client window the API way. It stores the old window function into the
GWL_USERDATA field of the window structure since it is needed in the
replacement window function, all messages need to be passed on to the
old window function. There is only one message of interest in this case
(the use of a Case results from the larger project, which handles more
than this message): WM_NCCALCSIZE. The window gets this message when
Windows tries to hide or show the scrollbars, among other cases. And it
arrives *before* there is any painting of the scrollbar. So we can
check if the window is going to sprout scrollbars and simply remove the
scrollbar styles again.

For the purists: there is no need to undo the subclassing before the
form is destroyed since the client window is destroyed before the form
object.

</quote>

Joe Cocker

unread,
Oct 5, 2003, 4:07:20 PM10/5/03
to
I picked the first approach, and it worked like a charm.

I can't thank you enough, Peter!


"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message

news:VA.0000a4d...@nomail.please...

0 new messages