Detailed analysis of MFC Window Location Management and examples in the window client area of a program written in MFC, there may be several subwindows (Windows with wm_child style ). The top is the toolbar, the middle is the View window, and the bottom is the status bar. The three windows coexist in the customer area of the framework and do not overlap with each other. The size of the main frame window has changed. Other subwindows can adjust their size in time to keep their positions unchanged. For example, the status bar window can always be at the bottom of the main frame client area, and its width is always the same as that of the main frame customer zone. The toolbar window can always be docked on one side of the main frame, and its width or height can always be the same as the width or height of the main frame customer area. The View window can always fill the remaining space in the main frame customer area.
If we derive a window class from the cwnd class and generate a window, several subwindows will be generated in its customer area. We want to make these subwindows arranged in a regular and non-overlapping manner, when the size of the parent window changes, the size and position of each child window can be adjusted in a timely manner, so that the proportional relationship between the positions and sizes of each child window remains unchanged. When one or several subwindows are moved, other subwindows can give up their positions in time. Of course, we can use the Management window function in API functions to compile our own method for managing subwindows. However, if you have a toolbar, Status Bar, and other subwindows in the parent window, can the subwindows you added work well with the subwindows provided by the MFC? How do you ensure that your subwindow does not overwrite the toolbar that can be docked everywhere? How can you know when the toolbar and status bar disappear, so that you can adjust your size in time to overwrite the space occupied by the toolbar and status bar? There is also a view in the customer zone of the window based on the document view framework. Can the child windows that you add hard on your own compete with the View window?
Therefore, you must understand the methods for managing the customer zone of the MFC window. In fact, the method for managing the client zone of the MFC window is very simple: the parent window calls a function, and the subwindow responds to a message, so much.
Cwnd: repositionbars function and wm_sizeparent message
First, let's briefly describe the process of allocating the customer space for the Child window in the MFC window: this process is completed by the parent window and the Child Window. The parent window first provides a region in the customer zone, which is called the start zone. Then, call a function. In this function, the parent window submits this area to the first subwindow through a message. The subwindow determines the size of the subwindow that you want to occupy, then, you can drag out the occupied part in the available area so that the available area is split. The parent window then submits the remaining available areas to the second child window through the same message, and the second child window switches out one piece as needed. In this way, each sub-window is split into one that you need. The remaining zones are used in the last sub-window. It can be seen that, except for the last subwindow, all other subwindows have to have their own algorithms in the message response function to determine the size of each subwindow in the available area, this algorithm is not needed because the last sub-window has no choice.
Of course, the initial available area is a rectangle, and the remaining available area after each cut is still a rectangle, which cannot be in other shapes.
For example, in a typical single-document program, the parent window is the main frame window derived from cframewnd, And the last sub-window is the View window. If csplitterwnd is used to generate separation bars, the last sub-window is the window with separators. Other subwindows are toolbar windows, status bar windows, and other control windows.
In a typical multi-Document Interface Program, the parent window is the main frame window, And the last child window is the window that covers the main window client area. The background is black-gray and has the child frame window containing the document, this is a window with a predefined window class. Its window class name is "mdiclient ". If csplitterwnd is used to generate a separator, the last subwindow is the one with the separator. Other windows are toolbar windows, status bar windows, and other control windows.
The function and message are: cwnd: repositionbars () and wm_sizeparent. This message is customized by MFC, not by windows.
This function and message are briefly described.
1. Function cwnd: repositionbars ()
This function is not a virtual function, so you cannot compile your own version by overwriting In the derived class. You can only understand its functions for flexible use.
To put it simply, this function places the available customer zone information in the message parameters of the message wm_sizeparent, and then enumerate all the subwindows in this window, send this message to each subwindow (removing a specific subwindow is equivalent to the last subwindow mentioned above, each subwindow that responds to this message will split the available customer zones. At last, adjust the size and position of the specific sub-window to the available area at the end.
2. Message wm_sizeparent
Each subwindow to participate in the distribution of customer areas must respond to this message, unless this subwindow is the specific subwindow.
There are at least two things to do in the subwindow to respond to this message: 1. Switch the available parent window customer partition to the one occupied by the customer. 2. According to the Message Parameter instructions, adjust the size and position of the message to fit in the area occupied by the message or do not adjust it.
The following describes in detail the function cwnd: repositionbars () and message wm_sizeparent.
1. Function cwnd: repositionbars () void repositionbars (uint nidfirst, uint nidlast, uint second, uint nflag = cwnd: reposdefault, lprect second = NULL, lpcrect lprectclient = NULL, bool bstretch = true );
There are many parameters, but they are quite understandable.
(1) nidfirst and nidlast
The ID range of the subwindow that participates in allocating the parent window customer area.
Each wm_child window has an ID, which is specified during window creation. The sixth parameter of the function cwnd: Create () is the ID. The hmenu type parameter in the createwindow and createwindow Wex functions. When the window style contains wm_child, it is not the menu handle, but the ID of the window.
The nidfirst and nidlast parameters indicate that, if the id value of a subwindow is greater than or equal to nidfirst and less than or equal to nidlast, wm_sizeparent messages will be sent to this subwindow in this function, this subwindow can be used to allocate the customer area in the parent window.
(2) nidleftover
As mentioned above, there is a specific subwindow that does not respond to the wm_sizeparent message. Only when other sub-windows are allocated is used up will the remaining sub-windows in the parent window be retrieved. Nidleftover is the ID of this subwindow. It must be greater than or equal to nidfirst and less than or equal to nidlast.
(3) lprectclient
This is a pointer to the rect structure data. This rect structure stores the initial available areas of the parent window customer zone. As the function sends the wm_sizeparent message to each subwindow in sequence, each subwindow that responds to the message will remove the portion occupied by it. The last part is the area that the subwindow with ID nidleftover will occupy. This parameter can be null, and the initial zone is the entire parent window customer zone.
(4) nflag and lprectparam
It is better to put these two parameters together. Nflag is the Function Identifier of the function. It can have three values: reposdefault, reposquery, and reposextra.
When nflag is equal to reposdefault, The repositionbars function sends wm_sizeparent messages to subwindows whose IDs are between nidfirst and nidlast and are not equal to nidleftover, each subwindow that responds to the Message removes the part occupied by it from the structure indicated by lprectclient, and adjusts its size and position to the size of the region occupied by it, finally, the repositionbars function also adjusts the size and position of the subwindow whose ID is nidleftover to the available area left by other subwindows, so that the subwindow completely overwrites the last available area. In this case, lprectparam is not required. It can be null.
When nflag is equal to reposquery, The repositionbars function sends wm_sizeparent messages to subwindows whose IDs are between nidfirst and nidlast and are not equal to nidleftover, each subwindow that responds to this message is segmented from the structure indicated by lprectclient, but they do not adjust their size and position, finally, the repositionbars function does not adjust the size and position of the subwindow whose ID is nidleftover. Instead, it performs an action based on the bstretch value. If bstretch is true, the repositionbars function copies the last available regions to the rect structure pointed to by lprectparam. If bstretch is false, then the repositionbars function puts the height and width of the available areas occupied by all other sub-Windows (this value makes sense only when all sub-windows are tightly arranged to form a large rectangle) copy to lprec The top and Left Members of the bottom and right members of the rect structure pointed by tparam are set to zero. The purpose of calling repositionbars with this nflag value is not to rearrange subwindows, but to see how many subwindows will occupy if subwindows are rearranged, the location of the last available area and so on.
When nflag is equal to reposextra, the function is similar to nflag when it is equal to reposdefault. In this case, lprectparam is used. As mentioned above, when nflag is equal to reposdefault, The repositionbars function will adjust the size and position of the subwindow with the ID of nidleftover to the available area left by other subwindows, make this sub-window completely overwrite the last available area. When nflag is equal to reposextra, before adjusting the size and position of the subwindow whose ID is nidleftover, repositionbars also uses lprectparam to correct the remaining available areas. If lprect points to the final zone, the correction is as follows:
Lprect-> top + = lprectparam-> top;
Lprect-> left + = lprectparam-> left;
Lprect-> right-= lprectparam-> right;
Lprect-> bottom-= lprectparam-> bottom;
With this correction, we can leave the remaining available areas empty for use instead of the sub-windows with the ID of nidleftover.
(5) bstretch
The role of this parameter has been mentioned above. It is mainly provided for subwindows that respond to wm_sizeparent messages. when determining the number of subwindows, such as the toolbar and status bar, you can determine how much space you want to move from the free space in the customer zone of the parent window, this parameter is also a basis for judgment. For details, see the onsizeparent function of the toolbar and status bar responding to wm_sizeparent ().
Processing Control bar position
Process and Algorithm for calculating the control bar position
The toolbar and other control bars are displayed in the parent border window as a child window. In order to handle the layout of the control bar (layout), the size of the control bar needs to be calculated first. This work is done by the control window such as the tool bar. Therefore, ccontrolbar provides two functions for this purpose: calcfixlayout and calcdynamiclayout. Both functions are virtual functions. Each derived class overwrites these two or one function to calculate its own size. These calculations are trivial and will not be discussed in detail here. Second, when the parent window position or size changes, adjust the size and position of the control bar accordingly.
Next, we will describe the steps for MFC to determine or update the toolbar, Status Bar, and other locations:
(1) When necessary, the border window calls the virtual function recalclayout to re-place its control bar and customer window. For example, when creating a window and responding to the Message wm_size (see section 5.3.3.5) border window initialization ).
(2) cframewnd: recalclayout calls the repositionbars function of cwnd to re-place the control bar window.
(3) cwnd: repositionbars:
Repositionbars first sends (send) The message wm_sizeparent used inside the MFC to each control subwindow, passes the rectangle pointer of the window customer area to them, and gives them a chance to confirm their size.
Then, each control subwindow uses onsizeparent to respond to wm_sizeparent messages. controlbar implements the message processing function onsizeparent, which calls calcdynamiclayout and other functions to determine the size of the window, and subtract the size from the rectangle of the customer area.
After all the control subwindows process the onsizeparent message, the repositonbars uses the returned information to call the calcwindowrect function to calculate the size of the customer window (MDI customer window, view, etc.
Finally, call: enddeferwindowpos or: setwindowpos to place all windows (control subwindows and customer windows ).
When the window is placed, wm_windowposchanging and wm_windowposchanged are sent. In the implementation of MFC, the control window responds to the previous message, and the message processing function is onwindowposchanging. Ccontrolbar, ctoolbar, and cstatusbar implement the onwindowposchanging message processing function.
In the functions involved in the above process, recalclayout is a virtual function defined by cframewnd; repostionbars is a member function of cwnd; calcawindowrect is a virtual function of cwnd; onsizeparent is the message processing function defined by ccontrolbar; onwindowposchanging is the message processing function defined by ctoolbar, cstatusbar, cdockbar, and other ccontrolbar Derived classes.
Next, we will analyze the two functions recalclayout and repositionbars.
Cframewnd virtual function recalclayout
The implementation of recalclayout is as follows:
Void cframewnd: recalclayout (bool bnotify)
{
// Whether recalclayout is being called
If (m_binrecalclayout)
Return;
M_binrecalclayout = true;
// Clear idle flags for recalc layout if called elsewhere
If (m_nidleflags & idlenostrap)
Bnotify = true;
M_nidleflags & = ~ (Idlelayout | idlenoay );
// Ole-Related Processing
# Ifndef _ afx_no_ole_support
// Call the layout hook -- Ole support uses this hook
If (bnotify & m_ppolicyhook! = NULL)
M_ppolicyhook-> onrecalclayout ();
# Endif
// Whether the border window of the floating control bar is included (cminiframewnd class)
If (getstyle () & fws_snaptobars)
{
// Calculate the position and size of the control bar and border window, and set their positions
Crect rect (0, 0, 32767,327 67 );
Repositionbars (0, 0 xFFFF, afx_idw_pane_first, reposquery,
& Rect, & rect, false );
Repositionbars (0, 0 xFFFF, afx_idw_pane_first, reposextra,
& M_rectborder, & rect, true );
Calcwindowrect (& rect );
Setwindowpos (null, 0, 0, rect. Width (), rect. Height (),
Swp_noactivate | swp_nomove | swp_nozorder );
}
Else
// If it is a common border window, set the position and size of all its subwindows.
Repositionbars (0, 0 xFFFF, afx_idw_pane_first,
Reposextra, & m_rectborder );
// This function has been processed
M_binrecalclayout = false;
}
This function is mainly used to call the repositionbars function. It calls the repositionbars function in two cases. One case is that the current border window is an inclusive window of the floating control bar (micro border window); the other case is that the current border window is a normal border window.
Repositionbars, a member function of cwnd
The repositionbars implementation is as follows:
Void cwnd: repositionbars (uint nidfirst, uint nidlast, uint nidleftover,
Uint nflags, lprect lprectparam, lpcrect lprectclient, bool bstretch)
{
Assert (nflags = 0 | nflags = reposquery | nflags = reposextra );
Afx_sizeparentparams layout;
Hwnd hwndleftover = NULL;
Layout. bstretch = bstretch;
Layout. sizetotal. Cx = layout. sizetotal. Cy = 0;
If (lprectclient! = NULL)
Layout. rect = * lprectclient; // obtain the customer zone from parameter 6.
Else
// The lprectclient parameter is null to obtain the customer region.
Getclientrect (& layout. rect );
If (nflags! = Reposquery)
// Prepare to place the subwindows (layout)
Layout. hdwp =: begindeferwindowpos (8); // reasonable guess
Else
Layout. hdwp = NULL; // not actually doing layout
// Send the resize message of the parent window to each control bar in a certain order;
// After receiving a message in each control bar window, deduct the region used by the control bar from the customer area;
// If necessary, call each control window: deferwindowpos
// Leave the remaining area to the nidleftover subwindow
For (hwnd hwndchild =: gettopwindow (m_hwnd); hwndchild! = NULL;
Hwndchild =: getnextwindow (hwndchild, gw_hwndnext ))
{
Uint NIDC = _ afxgetdlgctrlid (hwndchild );
Cwnd * pwnd = cwnd: fromhandlepermanent (hwndchild );
// If it is a specified nidleftover subwindow, save its window handle;
// Otherwise, the control bar window sends wm_sizeparent messages to them.
If (NIDC = nidleftover)
Hwndleftover = hwndchild;
Else if (NIDC> = nidfirst & NIDC <= nidlast & pwnd! = NULL)
// If Layout-> hdwp is not empty, onsizeparent will execute the window layout operation
: Sendmessage (hwndchild, wm_sizeparent, 0, (lparam) & layout );
}
// If it is reposquery, the client rectangle is obtained and the return value is
If (nflags = reposquery)
{
Assert (lprectparam! = NULL );
If (bstretch)
: Copyrect (lprectparam, & layout. rect );
Else
{
Lprectparam-> left = lprectparam-> Top = 0;
Lprectparam-> right = layout. sizetotal. CX;
Lprectparam-> bottom = layout. sizetotal. Cy;
}
Return;
}
// In other cases (reposdefault, reposextra), you need to perform the layout operation.
// Process hwndleftover (nidleftover subwindow)
If (nidleftover! = 0 & hwndleftover! = NULL)
{
Cwnd * pleftover = cwnd: fromhandle (hwndleftover );
// Allow extra space as specified by lprectborder
If (nflags = reposextra)
{
Assert (lprectparam! = NULL );
Layout. rect. Left + = lprectparam-> left;
Layout. rect. Top + = lprectparam-> top;
Layout. rect. Right-= lprectparam-> right;
Layout. rect. Bottom-= lprectparam-> bottom;
}
// Calculate the window size based on the customer size represented by layout. rect
Pleftover-> calcwindowrect (& layout. rect );
// Cause function: Call deferwindowpos
Afxrepositionwindow (& layout, hwndleftover, & layout. rect );
}
// Set size and layout for all windows)
If (layout. hdwp = NULL |! : Enddeferwindowpos (layout. hdwp ))
Trace0 ("Warning: deferwindowpos failed-low system resources./N ");
}
Repositionbars is used to change the size or position of the control bar in the customer window, where:
Parameters 1 and 2 define the range of Child Window IDs to be replaced, generally from 0 to 0 xFFFF.
Parameter 3 specifies a sub-window ID, which has the space left by the customer window. It is generally afx_idw_pane_first, which indicates the window ID of the view.
Parameter 4 specifies the operation type. The default value is cwnd: reposdefault, indicating that the window placement operation is executed. Parameter 5 is not used. If the value is cwnd: reposquery, it indicates that the window is placed (layout), but this operation is not performed, but the parameter 5 is initialized to the size of the customer zone. If the value is cwnd: reposextra, add the value of parameter 5 to the customer area of the subwindow represented by parameter 2 and place the window.
Parameter 6 indicates the size of the available window customer area passed to the function. If it is null, the size of the window customer area is used.
If you execute the layout operation, the core processing of this function is:
First, call: begindeferwindowpos to initialize a multiple-window-Position Structure hdwp in windows;
Then, call the following sub-windows one by one: deferwindowpos to update hdwp. Before calling: deferwindowpos, you must determine the size of the subwindow. These tasks are completed by sending a message wm_sizeparent to each control subwindow.
The control subwindow uses the onsizeparent function to respond to the wm_sizeparent message. First, determine the size of the subwindow. Then, if window layout is required (the wm_sizeparent Message Parameter lparam contains a non-empty hdwp structure (lplayout-> hdwp), onsizeparent calls the afxrepositionwindow function to calculate the location of the current control window, save the result to hdwp.
After the size of all control windows is determined, the remaining windows are left with hwndleftover (if any ). After determining the hwndleftover size, call the afxrepositionwindow function to calculate its location and save the result to hdwp.
The above-mentioned function afxrepositionwindow is called indirectly: deferwindowpos.
Finally,: enddeferwindowpos: Uses hdwp to arrange the positions and sizes of all subwindows.
Other functions, such as onsizeparent, onwindowposchanging, and calcwindowrect, are not further analyzed here.