A Simple Dialog Layout Manager

Thanks to Marc Flerackers for updating the Article.

If you often use Dialogs and want to resize them you will have
noticed, that there is no feature in the MFC that helps you. You
have to do it by hand. Maybe you have seen the GridBagLayout in
Java(tm), then this one will look familiar to you. The idea
behind is to split the dialog in Panes. Each pane has some
attributes an can also contain Panes itself. Resizing your Dialog
can now be as easy as requesting the root pane to fit in the
current client area! Actually this is done automatically in the
OnSize handler, so you just have to define the layout.


  1. Add DialogMgr.cpp to your current project (e.g.
    Menu Project->Add to project->Add files)
  2. Create a new dialog or skip to the next point if you
    already have one. I assume this class to be named CMyDialog.
    (Note: Do not forget to make the Dialog resizable, i.e.
    in the Dialog Editor change the border style to resizing)
  3. Change the Base of your Dialog from CDialog to CDialogMgr
    (simple search for all references to CDialog and replace
    them), do not forget to include the header.
  4. In your class Declaration add a DECLARE_LAYOUT like this:
    class CMyDialog : public CDialogMgr
    	CMyDialog(CWnd* pParent = NULL);   // standard constructor
  5. Now comes the really important step: You have to define
    what your Layout looks like. Assume this to be your
    Dialog template (I added the ControlIDs as red text
    because I will refer to them later):

    If we want to have this Dialog layouted automatically, we
    have to define the panes. In this example I decided to
    define the panes like this:

    The root pane is marked red and contains 5 Items. The 2nd
    and 4th Items are Panes themself (marked blue), which
    contain respectively 2 and 3 Items. To define this layout
    you would have an OnInitDialog like this:

    BOOL CMyDialog::OnInitDialog()
    	// define the Layout
    	CPane *newItemPane=new CPane ( this, HORIZONTAL );
    First of all the first Subpane is created. It is a horizontal pane. Next you add the Items:
    	newItemPane->addItem ( IDC_NEW_ITEM, GREEDY );
    	newItemPane->addItem ( IDC_ADD_ITEM, NORESIZE );
    The second SubPane is created similar:
    	CPane *bottomPane=new CPane ( this, HORIZONTAL );
    	bottomPane->addItem ( paneNull, GREEDY );
    	bottomPane->addItem ( IDOK, NORESIZE );
    	bottomPane->addItem ( IDCANCEL, NORESIZE );
Note the use of paneNull, this is not an actual Pane but a placeholder. The Term greedy in this context means that it will eat up all space that is left. The second Item is the OK Button, the CPaneBase::NORESIZE means, that it's  size is fixed. The actuall size could be appended as additional parameters, but if you leave them it will take the size from the current size of the control.    An Item can have several mode flags. A flag of 0 like in the first line  denotes a greedy Item, you could also add Items with an absolute size or some items with a relative size in percent. The Layout Engine first allocates space to ABSOLUTE_* items. What is left will  be allocated to RELATIVE items and the remaining space is divided into equal pieces for greedy items.

	m_pRootPane=new CPane ( this, VERTICAL );
Now the root pane is created (as denoted by m_pRootPane).
	m_pRootPane->addItem ( IDC_NEW_ITEM_STATIC, NORESIZE );
	m_pRootPane->addPane ( newItemPane, ABSOLUTE_VERT );
	m_pRootPane->addItem ( IDC_ITEM_LIST_STATIC, NORESIZE );
	m_pRootPane->addItem ( IDC_ITEM_LIST, GREEDY );
	m_pRootPane->addPane ( bottomPane, ABSOLUTE_VERT );
We want the 4th item (the ListBox) to grow and shrink. The 2nd and 5th item will preserve their height, but the panes will size in the horizontal direction. We use a special feature here: ABSOLUTE_VERT denotes that the vertical  size is fixed. As we did not provide one, the maximum height of all Items of the panes are computed and used as the fixed height.    
	UpdateLayout ();
Do not forget to call this one or the Layout will only get effective when  you resize the Border.    

	return TRUE;  // return TRUE unless you set the focus to a control
		      // EXCEPTION: OCX Property Pages should return FALSE
  1. Optionally: Add a call to Load() and Save() to (Re-)Store
    the Dimensions in/from the registry.
  2. That’s it! Now the Dialog is automatically resized. Note
    that it also computes a minumum tracking size! Please
    take a look at the headerfile (DialogMgr.h) as all the
    features and functions are described there.

Version 1.2

Reworked the support for TabCtrls and added some more
constants. Added the ability for somewhat more complicated
layouts. Consider this example:

What is different here? First of all we would like to have the
ListCtrl placed inside the tab, which is a pane. So I derived a
new class from CPane named CPaneTab, which also resizes a TabCtrl
along with its own size and places all childs within the tab (see
CTabCtrl::AdjustRect() ). It also manages the minimum tracking
size so that the Tabs are always visible! The code to describe
the layout above looks like:

	// define the layout

	// the bottom pane
	CPane*	paneBottom = new CPane(CPane::HORIZONTAL);
	paneBottom->addItem( new CPaneItem( NULL, CPaneBase::GREEDY));			// Greedy
	paneBottom->addItem( new CPaneItem( IDOK, this, CPaneBase::NORESIZE));
	paneBottom->addItem( new CPaneItem( IDCANCEL, this, CPaneBase::NORESIZE));

	// (green) holding a spacer and the ListCtrl
	CPane*	paneList = new CPane(CPane::VERTICAL);
	paneList->addItem( new CPaneItem( &m_list, CPaneBase::GREEDY));

	// (blue) the pane with the tab attached with an ExtraBorder
	CPane* paneTab  = new CPaneTab(&m_tab, CPane::HORIZONTAL);
	paneTab->addItem( paneList );

	// (red) the root pane 
	m_pRootPane = new CPane(CPane::VERTICAL);
	m_pRootPane->addItem( paneTab );
	m_pRootPane->addItem( paneBottom, CPaneBase::ABSOLUTE_VERT );


Download demo project – 81 KB

Date Last Updated: April 5, 1999

More by Author

Must Read