Creating an Expanding Dialog

–>

Expanding dialogs are useful if you want to present users
with lots of options, but only if they select an appropriate option, or show results of a
task only when the task has started. Expanding dialogs expand and contract according to
states of controls, usually push buttons or check boxes.

An example of an expanding dialog is the Windows colour
picker dialog which expands to give you more control over your choice of colour, but only
if you click "Define Custom Colors". The example below expands when the Find
button is clicked to show the progress of the search, but all you need to do is to change
the list box with another control you wish to be there.

To start with, in order for the expanding dialog to work
automatically, the dialog must contain two special controls that will control the
expansion. Below is the finished dialog in design mode which shows the important controls,
and the tab order, which is also important:

The important controls are related to by their tab order
number here:

3: The Expand – Contract control which
controls this process. This can be any type of push button control such as a check box, a
push button, or what-ever. Call this control IDC_EXPAND.

5: The expand border marker which shows
where the expansion will take place. The best control I have found for this is the picture
control stretched thin, and left in frame mode, but with the sunken style. This control
does not have to be visible, but it must be on the border where the expansion will take
place. Call this control IDC_EXPAND_BORDER.

Important: The tab order must be as shown above,
with the controls in the expanded section of the dialog after the expand border marker.

When you have completed this, create a class for the dialog
called CExpandDialog, derived from the usual CDialog class, and add a handler for
the dialog’s WM_INITDIALOG message, and add an entry for the IDC_EXPAND control
BN_CLICKED, or what-ever is appropriate for the control. Then add a member variable of
control type for IDC_EXPAND_BORDER. The default class is CStatic, although we need CWnd,
but we will change it later. Call this member m_Devide.

Close ClassWizard, and open the include file for the
dialog(ExpandDialog.h). Under the // Dialog Data label, you should find CStatic
m_Devide.
Change this to CWnd m_Devide.

Note: From now on, type in italics only has to be
added. Other code shown should already exist.

Under the protected Implementation section, add these lines:

void ContractDialog();
void EnableExpandedControls(BOOL bEnabled);

int m_nNormalHeight; int m_nExpandedHeight; BOOL m_bExpanded;

We’ve now finished with the include file, so close it and save it.

Open the implementation file for the dialog (ExpandDialog.cpp), and
in the class constructor

CExpandDialog::CExpandDialog(CWnd* pParent /*=NULL*) : CDialog(CExpandDialog::IDD, pParent) { m_nNormalHeight = 0; m_nExpandedHeight = 0; m_bExpanded = TRUE; //{{AFX_DATA_INIT(CExpandDialog) //}}AFX_DATA_INIT }

When the dialog is initialized, we want the dialog to be in
its contracted state, so that the advanced controls are hidden.

So to do that, we add the code below:

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

 ContractDialog();

 return TRUE;
}

The IDC_EXPAND button’s OnExpand() message handler now needs
to contain this code to toggle the dialog between the expanded
and normal states:

void CExpandDialog::OnExpand()
{
 CRect rcDlg;

 GetWindowRect(rcDlg);

 if (m_bExpanded)
 {
  rcDlg.SetRect(rcDlg.left, rcDlg.top,
   rcDlg.left + rcDlg.Width(),
   rcDlg.top + m_nNormalHeight);
 }
 else
 {
  rcDlg.SetRect( rcDlg.left, rcDlg.top,
   rcDlg.left + rcDlg.Width(),
   rcDlg.top + m_nExpandedHeight);
 }

 MoveWindow(rcDlg, TRUE);

 m_bExpanded = !m_bExpanded;

 EnableExpandedControls(m_bExpanded);
}

This function uses the m_bExpanded flag to determine whether
the dialog should be in its expanded or contracted state. It then uses MoveWindow
appropriately to modify the height of the dialog. After re-sizing, the extra controls are
either enabled or disabled, again, depending on the state the dialog is in.

Now we have to implement the EnableExtraControls() function.
We have already defined the function in the include file, so we just need to add the code
below in its entirety:

void CExpandDialog::EnableExpandedControls(BOOL bEnabled)
{
 HWND hWndChild = ::GetDlgItem(m_hWnd, IDC_EXPAND_BORDER);

 while (hWndChild != NULL)
 {
  ::EnableWindow(hWndChild, bEnabled);
  hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT);
 }
}

This is why the tab order is so important. It enables or
disables all the controls after the expand border.

The ContractDialog() function initializes the dialog in its
contracted state, so that the advanced or extra controls aren’t visible. Again, as we have
already defined the function, we just have to add this code:

void CExpandDialog::ContractDialog()
{
 CRect rcDlg, rcMarker;
 GetWindowRect(rcDlg);
 m_nExpandedHeight = rcDlg.Height();

 m_Devide.GetWindowRect(rcMarker);

 m_nNormalHeight = (rcMarker.top - rcDlg.top);

 rcDlg.SetRect( rcDlg.left, rcDlg.top,
  rcDlg.left + rcDlg.Width(),
  rcDlg.top + m_nNormalHeight);

 MoveWindow(rcDlg, TRUE);

 m_bExpanded = FALSE;

 EnableExpandedControls(m_bExpanded);
}

The code is now complete, so you can now compile and run it.

When you click IDC_EXPAND, the dialog should expand.

This code does not change the button caption of IDC_EXPAND,
accordingly to the state of the dialog which is probably preferably, and it also does not
change the caption of the dialog, which could show what state the dialog is in.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read