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: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • CentreCorp is a fully integrated and diversified property management and real estate service company, specializing in the "shopping center" segment, and is one of the premier retail service providers in North America. Company executives travel a great deal, carrying a number of traveling laptops with critical current business data, and no easy way to back up to the network outside the office. Read this case study to learn how CentreCorp implemented a suite of business continuity services that included …

Most Popular Programming Stories

More for Developers

RSS Feeds