Introduction
The inspiration for this coding project was the result of a) Paolo Messina's
article on his CResizableDialog code, and b)
me mentioning I had adapted a CFormView
to use a
CResizablePage
in an embedded property sheet.
The CResizablePage
in a formview was a minor exercise compared
to this. What I've come up with is a CResizableFormView
. This code
is based entirely on Paolo's CResizableDialog
code.
If you're not interested in how I did it, Skip to the bottom of this article
to see how to implement it yourself.
The Concept
It seemed to me that if a dialog box could benefit from this
technique, then so could a formview, because after all, a formview is nothing
more than a view that acts like a dialog box. It even uses a resource template
like a dialog box.
Initially
The initial coding was fairly straightforward. I created a
CFormView-derived class called
CResizableFormView
. Next, I copied
all of the utility functions (functions not contributed by ClassWizard). Then, I
evaluated the need/availability of some of the windows messages Paolo's code was
using. I ended up only needing to handle the
WM_SIZE
command.
Lastly, I decided that the min/max window size code was not applicable to a
formview, so I deleted the code involving that particular aspect of
CResizableDialog
.
Implementation
After getting the code to compile, I tried deriving a new
class based on my new
CResizableFormView
. After some
experimentation, I discovered that I could not use the
AddAnchor
function until AFTER the
InitialUpdate
had completed. If I didn't
follow this rule, the controls would not resize correctly.
void CFormview2View::OnInitialUpdate()
{
CResizableFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
AddAnchor(IDC_LIST1, TOP_LEFT, BOTTOM_RIGHT);
AddAnchor(IDC_GROUP1, TOP_LEFT, BOTTOM_LEFT);
}
Minor Issues
After playing around with resizing for a while, I
discovered that if I made the view window smaller than the original dialog
template, a couple of bad things happened involving the controls that were
"anchored".
- The listbox disappeared altogether and would only partially appear as I made
the view horizontally larger.
- The groupbox around the radio controls would just keep resizing until
nothing was left (of the groupbox).
It was obvious at that point that I had to include some code to restrict the
controls from resizing once they reached (or were smaller than) their original
size. I also assumed that I had to be mindful of getting the correct original
size, meaning I had to determine each control's size before the window was
allowed to be resized the first time. Assuming that the programmer could decide
to do something other than use a call to MFC's ResizeParentToFit
, I
had to make sure to get the control's info immediately after the view was
created, but before it was resized. I wanted to impact as little of Paolo's
original code as I could manage, so the fix was a while coming.
To remedy the situation, I created a CTypedPtrArray
that held a
new structure. This structure contains the original size and location of each
"anchored" control, as well as its HWND
and control ID. To support
the structure, I had to also write a couple of new functions. Here are the
details:
void CResizableFormView::AddResizedControls()
This function is responsible for determining the HWND
and window
size / location of the specified control, as well as storing its anchor
positions. The parameter list is identical to the original
AddAnchor
function. Once all of this information was gathered, the
control was added to the new CTypedPtrArray
for safe-keeping.
void CResizableFormView::AnchorControls()
This function actually calls AddAnchor
for all of the controls
the programmer specified when he/she called the AddResizedControls
function. The significance of this will be evident later.
Results
In order to implement the sizing/positioning fix, A new order of
execution was required in the
CResizableFormView
-derived class. The
OnInitialUpdate
function now looks like this:
void CFormview2View::OnInitialUpdate()
{
AddResizedControl(IDC_LIST1, TOP_LEFT, BOTTOM_RIGHT);
AddResizedControl(IDC_GROUP1, TOP_LEFT, BOTTOM_LEFT);
CResizableFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
AnchorControls();
}
But what if you wanted the view to NOT resize itself according to the dialog
template. That's easy. Just calculate the size of the parent window of the
formview, and call MoveWindow
before exiting
OnInitialUpdate
. Here's an example:
void CFormview2View::OnInitialUpdate()
{
AddResizedControl(IDC_LIST1, TOP_LEFT, BOTTOM_RIGHT);
AddResizedControl(IDC_GROUP1, TOP_LEFT, BOTTOM_LEFT);
AddResizedControl(IDC_IPADDRESS1, BOTTOM_RIGHT);
CResizableFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
CRect rectFrame;
CFrameWnd* pFrame = GetParentFrame();
ASSERT_VALID(pFrame);
pFrame->GetWindowRect(rectFrame);
CSize size = rectFrame.Size();
ResizeParentToFit(FALSE);
AnchorControls();
pFrame->MoveWindow(rectFrame, TRUE);
}
The Final Word
To implement the CResizableFormView
, perform the following
steps:
- Create a
CFormView
class and its dialog template if necessary.
- Change the base class of your
CFormView
-derived class to
CResizableFormView
.
- #include "ResizableFormView.h" in your derived formview's header file.
- If the function isn't already there, use ClassWizard to override the
OnInitialUpdate
function.
- Call
AddResizedControls
for each control that will be
resized/repositioned. Do this BEFORE the call to the base class'
OnInitialUpdate
.
- Call
AnchorControls
. Do this AFTER the call to
ResizeParentToFit