Auto repositioning/resizing of child controls (using C++ templates)

Yet, another method to reposition/resize child windows within their parent framework. Although the subject received a lot of attention, including this web site, I invite you to look at the following method as it presents certain advantages over the others I already know.

First of all this is not a library. There is no associated DLL or LIB to link to.
Secondly, it is not a class hierarchy. You dont have to derive your classes from
classes exposing this functionality. Often, you already have a hierarchy of classes
designed for a certain behavior. This behavior does not include repositioning/resizing
of child controls, but you want to add it. Since the MFC doesnt let you use multiple
inheritance when it comes to windows, you might wonder what is the solution. Well, the
solution is templates. GEOMETRY is actually a collection of templates designed to make
your windows to resize gracefully.

The interface to GEOMETRY is a single class template CGeometryWnd. You can use this class template with all your window classes including dialogs, forms, property sheets/property pages and splitter windows. Being a template library, GEOMETRY is contained in a single file Geometry.h. All you have to do is to include this file, to use CGeometryWnd and possibly a series of macros, in relation with your classes.

For example to make a dialog resizeable you need to:


  1. make your dialog in resource editor resizeable (replace the dialog frame with a resizeable border)

  2. specify the rules to apply to child controls (either in the resource editor or in the source code)

  3. include in dialogs source code Geometry.h

  4. use the template CGeometryWnd over your dialogs class

Ex:


#include Geometry.h
USE_GEOMETRY_TEMPLATES;
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
DECLARE_GEOMETRY_RTSUPPORT()

public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

virtual BOOL OnInitDialog();
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
// No message handlers
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();

AddConstraint(IDOK,CConstraint("X"));
return TRUE;
}

// App command to run the dialog
void CTestPSGeoApp::OnAppAbout()
{
CGeometryWnd<CAboutDlg> aboutDlg;
aboutDlg.DoModal();
}

The constraints added to child windows, control their behavior
in case the parent resizes. The constraints can be added at
design time (aka in resource editor, see note) or at runtime
using the DECLARE_GEOMETRY_RTSUPPORT() macro and
AddConstraint()function. The constraint object has two
important constructors:


CConstraint(LPCSTR lpOptions) and
CConstraint(float x,float y=0,float cx=0,float cy=0)

In the first case you can specify the repositioning/resizing of the control by using a control string (see table for details). Basically the string format is:

Option1[value]+Option2[value]+

Where Optionx (see table below) controls the
position/size of the control and [value] ([]
are not optional) is an optional parameter
specifying the amount (between 0 and 1 as a
float number) with which the control will
reposition/resize from the total amount of
the parent resize value.























































Option


Meaning


Comment


X


Reposition on x axis


Control will move on the x axis


Y


Reposition on y axis


Control will move on the y axis


CX


Resize on x axis


Control will resize its width


CY


Resize on y axis


Control will resize its height


x


Stay on x axis center


Equivalent with X[0.5]


y


Stay on y axis center


Equivalent with Y[0.5]


cx

 

Equivalent with CX[0.5]


cy

 

Equivalent with CY[0.5]


MX


Maintain aspect on x axis


Preserves the X/CXParent ratio


MY


Maintain aspect on y axis


Preserves the Y/CYParent ratio


MCX


Maintain aspect on the width


Preserves the CX/CXParent ratio


MCY


Maintain aspect on the height


Preserves the CY/CYParent ratio


Note:

To add the constraints at design time directly in
resource editor you ll gonna surround the controls
sharing the same constraint options with invisible
group boxes having the caption in the form "
$Geometry:Option1[value]+Option2[value]+"

w/o the quotes. Please note the $Geometry in front
of the options string, this is not optional. Be careful
with the capital/minor letters as they have different
significance. Using this form of constraints specification
you dont need DECLARE_GEOMETRY_RTSUPPORT() and AddConstraint()
as previously displayed.

Using the second constructor you will specify the
values between 0 and 1 that will be multiplied with
the total resize amount of the parent. A value of 0 means
dont move/resize a value of 1 means full
movement/resizing. Please note that the values
of movement/resizing on one axis added together
must not be greater than 1 (otherwise the control
will be clipped away from its parent).

AddConstraint(UINT nIDC,CConstraint& constraint) takes
as parameters the control ID and a reference to a constraint object.

To achieve dynamic creation of windows MFC uses
DECLARE_DYNCREATE(), IMPLEMENT_DYNCREATE() along
with RUNTIME_CLASS() macros. For example the CFormView
derived classes are usually created by the framework
in this way. To accommodate this case GEOMETRY uses a
set of macros of its own:


DECLARE_GEOMETRY_DYNCREATE(yourClass)
IMPLEMENT_GEOMETRY_DYNCREATE(yourClass)
GEOMETRY_RUNTIME_CLASS(yourClass)

The first macro is to be used in the definition of the class (however outside the class scope), the second in the implementation of the class and eventually the third instead of RUNTIME_CLASS(). The first two dont replace DECLARE_DYNCREATE(), IMPLEMENT_DYNCREATE() macros, they still have to be used.

For example:


#include <Geometry.h>
USE_GEOMETRY_TEMPLATES;
class CTestPSGeoView : public CFormView
{
protected: // create from serialization only
CTestPSGeoView();
DECLARE_DYNCREATE(CTestPSGeoView)

};

DECLARE_GEOMETRY_DYNCREATE(CTestPSGeoView);

In the definition file of the CTestPSGeoView class and


IMPLEMENT_DYNCREATE(CTestPSGeoView,CFormView)
IMPLEMENT_GEOMETRY_DYNCREATE(CTestPSGeoView)

In the implementation file.

Also, there is necessary the substitution:


CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_TESTPSTYPE,
RUNTIME_CLASS(CTestPSGeoDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
GEOMETRY_RUNTIME_CLASS(CTestPSGeoView));

Thats it! An example along with the full source code is
provided. You may use freely this template library, even modify
it, but at your own risk and specifying where the code comes
from. The template library was built and tested on VC 6.0 only.

Download demo project – 36 KB

Date Last Updated: February 4, 1999

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read