The
sample
application uses a fairly
unknown message:
WM_SIZEPARENT declared in
afxpriv.h. But first, see
how
MFC frame
windows maintain the child
windows' layout. The WM_SIZE
handler implementation calls
RecalcLayout. In
turn,
RecalcLayout
calls
RepositionBars.
Do not let the function name
full you; it actually should
be called RepositionChildren
because this is exactly what
that function does. It
enumerates all child windows
and sends a message to
resize all.
To
enumerate all child windows,
it calls
GetNextWindow
after retrieving top
child—in other words, the
child that is first in the Z
order; so, the search starts
with the first child window
that is created and goes
down to the last one. To
resize windows, the function
uses
BeginDeferWindowPos,
DeferWindowPos,
and
EndDeferWindowPos;
they resize all children at
the same time, thus reducing
flickering. For each child
window, RepositionBars sends
the above-mentioned
WM_SIZEPARENT message to
allow each child to decide
what area to occupy and add
itself to a
DeferWindowPos
path.
There
is one exception. The view
with the magic
AFX_IDW_PANE_FIRST ID is put
aside until the end, and
resized after all other
children are already
positioned, taking whatever
real estate is left for it.
By the way, the
AFX_IDW_PANE_FIRST ID is
used by the default
implementation of the
RecalcLayout.
RecalcLayout can
be overridden and other IDs
can be designated as the
special one.
How
do other child windows know
that certain areas of the
parent's client have already
been taken by other
children?
MFC
is using its own structure
to carry that information
from the first child to the
last one:
AFX_SIZEPARENTPARAMS. MSDN
is quite shy about
documenting it. It has the
following members:
-
HDWP
hDWP
-
RECT rect
-
SIZE sizeTotal
-
BOOL bStretch
-
hDWP:
A handle returned by
BeginDeferWindowPos
-
rect:
A rectangle that
represents the parent's
client area trimmed by
other children
Basically, it is an area
that is available for taking
the following:
-
sizeTotal:
Represents the size of
the child window (width
and height) after
repositioning.
-
bStreech:
Tells the window to
stretch itself to take
all available space. Can
be ignored by
non-toolbar windows.
How
this structure is used is
totally up to the
implementation.
View
windows should be created
after all toolbars and the
status bar are created
(remember the Z order). The
best place to do this is in
the WM_CREATE message
handler. A child window with
AFX_IDW_PANE_FIRST can be
created anytime.
How to Use the
AFX_SIZEPARENTPARAMS
Structure
When
the
frame window
is created, it receives the
WM_CREATE message. Before
toolbars are created, the
base class calls
OnCreateClient where a view
registered with the document
template is created. After
it returns, it is time to
create control bars if
desired. I have included a
child toolbar and commented
out a part of the code that
creates a status bar.
Uncomment it if you want to
see status bar too.
After
the control bars are done, I
placed a call to CreatePanes
to creates other
views. To
simplify, I added two
views. This order
assures that the
WM_SIZEPARENT message will
be sent in the proper order.
This
sample only
illustrates how to use this
message and the
AFX_SIZEPARENTPARAMS
structure. Each layout
design will require a
different approach; however,
it is very similar to what
is presented here.
After
the control bars have
decided what part of the
client area of the frame
window to take, the
framework sends a
WM_SIZEPARENT message to the
first additional pane
(CTitleFormView) that serves
as a kind of title. The
height of this window does
not change and is calculated
in the view's OnCreate
handler. In sizing the
message handler, the window
takes half of the width of
the available client area
and sets its own heights; it
uses a lpLayout structure to
pass those values to the
next window and sets the
sizeTotal member of the
structure to own rectangle
size. After all
calculations, the view calls
AfxRepositionWindow that
calls
DeferWindowPos,
adding the view's parameters
to a defer path. If the
handle to the defer window
position is null, it only
performs calculations
without calling
AfxRepositionWindow.
Now,
a message is sent to a
CDisplayView. It uses values
from the lpLayout structure
to position itself in the
lower part of the right half
of the client area and
adjusts the rect member of
the structure to show the
leftover area that will be
taken by the main
view—AFX_IDW_PANE_FIRST. As
a result of passing the
document pointer when panes
were created, all views
share the same document
object; therefore,
UpdateAllViews
can be called to update all
panes. It happens upon the
list view selection change.
That is a reminder showing
how the doc/view
architecture works.
Conclusion
There
is no additional class that
could be included in other
projects. This article
serves as an example,
showing how to use
WM_SIZEPARENT message as
well as explaining
(documenting) the
AFX_SIZEPARENTPARAMS
structure.
Downloads
MDILayout_Sample.zip