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.



Comments

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

Top White Papers and Webcasts

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today makes data protection a must-have, as we live in a data-driven society -- the digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join Seagate Cloud …

  • Managing your company's financials is the backbone of your business and is vital to the long-term health and viability of your company. To continue applying the necessary financial rigor to support rapid growth, the accounting department needs the right tools to most efficiently do their job. Read this white paper to understand the 10 essentials of a complete financial management system and how the right solution can help you keep up with the rapidly changing business world.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds