Method to achieve repositioning/resizing of child controls

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 don't 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 doesn't 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:
  • 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 dialog's source code Geometry.h
  • 4. use the template CGeometryWnd over your dialog's class
  • Example:
    
    #include 
    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 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 don't 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 don't 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 don't replace DECLARE_DYNCREATE(), IMPLEMENT_DYNCREATE() macros, they still have to be used.

    For example:

    
    #include 
    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));
    
    That's 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: April 4, 1999



    Comments

    • Method to achieve repositioning/resizing of child controls

      Posted by Legacy on 10/17/2001 12:00am

      Originally posted by: Francesco

      The code doesn't work with ActiveX Controls.
      I get HWND hwnd = NULL from a call to GetDlgItem(hwndParent,m_rsctl.m_nID) where m_rsctl.m_nID is the control ID of the ActiveX inserted into the dialog.

      inline void CBindCtlToConstraint::ResizeControl(HWND hwndParent,CSize pExt,CSize mExt)
      {
      CRect rCtl;
      CWnd* pParent = CWnd::FromHandle(hwndParent);
      -> HWND hwnd = GetDlgItem(hwndParent,m_rsctl.m_nID);
      ASSERT(hwnd != NULL);
      ...
      ...
      }

      Reply
    • Had to make some changes to redraw invalidated rectangle.

      Posted by Legacy on 05/06/1999 12:00am

      Originally posted by: Ken Varn


      I had some problems with rectangle invalidation. Redrawing of the window contents was not being performed correctly after resizing a control (particularly a frame control in my case). I had to modify the following snippets of code to accomodate rectangle invalidation and to allow affected controls to repaint:

      First, I had to comment in the call to InvalidateRects() at the bottom of the member function:

      inline void CBindCtlToConstraint::ResizeControl(HWND hwndParent,CSize pExt,CSize mExt)
      {

      .... SNIP ....

      UINT nFlags = SWP_NOZORDER | ((GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE)? SWP_SHOWWINDOW : 0);
      //ShowWindow(hwnd,SW_HIDE);

      SetWindowPos(hwnd,NULL,x,y,cx,cy,nFlags);

      InvalidateRects(pParent,rCtl,CRect(x,y,x+cx,y+cy));
      }


      Next, I had to modify the InvalidateRects() member function as follows (original code commented out for illustration):

      inline void InvalidateRects(CWnd* pWnd,CRect irect,CRect frect)
      {
      // irect is the initial rect of the control
      // frect is the final rect of the control (in parent's coordinates)
      CRect drect;
      // static CRect prect;

      // if (frect.TopLeft() != irect.TopLeft())
      // return;

      // irect.IntersectRect(prect,irect);
      // drect.UnionRect(irect,frect);
      // pWnd->InvalidateRect(drect);
      // drect.IntersectRect(irect,frect);
      // drect.DeflateRect(0,0,3,3);
      // pWnd->ValidateRect(drect);

      // pWnd->GetClientRect(prect);

      drect.IntersectRect(frect,irect);
      pWnd->InvalidateRect(drect);
      }

      Reply
    • Demo won't compile.

      Posted by Legacy on 04/07/1999 12:00am

      Originally posted by: John M. Drescher

      The demo program would not compile on my NT BOX in VC6.0 unless I changed #include <Geometry.h> in stdafx.h to #include "Geometry.h".

      Reply
    Leave a Comment
    • Your email address will not be published. All fields are required.

    Top White Papers and Webcasts

    • Best-in-Class organizations execute on a strategy that supports the multi-channel nature of customer requests. These leading organizations do not just open up their service infrastructures to accommodate new channels, but also empower their teams to deliver an effective and consistent experience regardless of the channel selected by the customer. This document will highlight the key business capabilities that support a Best-in-Class customer engagement strategy.

    • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

    Most Popular Programming Stories

    More for Developers

    Latest Developer Headlines

    RSS Feeds