Expanding/Contracting Dialog Box

In a moderately complex application, the user is presented with the task of learning the features of the software. It is useful, in some cases, to limit the features that a beginning user has access to; also, an advanced user will appreciate not being inundated with a large number of controls on a dialog, but only the ones that are used most frequently. Hence the need for an expanding and contracting dialog.

The premise of the expanding/contracting dialog is to present a set of controls on a dialog to the user, and then allow access to the rest of the "advanced" controls if the user clicks the appropriate button to expand the dialog. A sample of this is as follows:

The class presented here fits between your dialog class and CDialog, so that the expanding/contracting actions can be automatically managed. There is minimal change to be made to your existing dialog class code, so it is easy to add and remove this expanding/contracting feature to your dialogs.

This code should be UNICODE correct (it has not been tested), and compiles warning free under VC++5 using MFC 4.2.

Instructions

  1. add the ExpandingDialog.h and ExpandingDialog.cpp files to your project.
  2. add a static frame to your dialog (in VC++, this is the "picture control") for the default box, and give it a unique resource ID.
  3. add a button to your dialog which will be the "Advanced" button (the one that expands and contracts the dialog), and give it a unique resource ID.
  4. in your dialog's .h and .cpp files, replace all occurrences of CDialog with CExpandingDialog.
  5. in your dialog's .h file, put a "#include ExpandingDialog.h" above your dialog's class declaration.
  6. modify the constructor of your dialog in your dialog's cpp file, to look something like the following:
    
    CMyDialog1::CMyDialog1(CWnd* pParent) : CExpandingDialog(CMyDialog::IDD,
    pParent,IDC_DEFAULTBOX,IDC_ADVANCEDBUTTON, _T("Advanced >>"),
    _T("Advanced <<"))
    
    (In this example, IDC_DEFAULTBOX was the ID given for the static frame in step 2, and IDC_ADVANCEDBUTTON was the ID given for the button in step 3.)

That is all that needs to be done; recompile and run your application, and the expanding/contracting dialog should work correctly. For extended capabilities, please read the documentation for the utility and virtual functions listed below.


Class Members

Constructor:

CExpandingDialog(
   UINT nIDTemplate,
   CWnd* pParent, 
   int nIDFrame,
   int nIDButton,
   LPCTSTR strExpand = _T("Advanced >>"), 
   LPCTSTR strContract = _T("Advanced <<"),
   BOOL bAllowContract = TRUE)

Normally, the constructor is called from the implementation of your dialog's constructor. The first two parameters are the same as those taken by CDialog. The rest are specific to CExpandingDialog, are are as follows:


int nIDFrame 

the resource ID of the static frame that defines the boundaries of the contracted state of the dialog.


int nIDButton 

the resource ID of the button that will be used to expand and contract the dialog


LPCTSTR strExpand and LPCTSTR strContract 

the strings that will be put on the button identified by nIDButton, when the dialog is in its contracted and expanded state, respectively.


BOOL bAllowContract

if TRUE, the dialog will allow the user to both expand and contract the dialog. If FALSE, the user may expand the dialog, but may not ever contract it again. This flag does *not* affect the operation of the Expand() function; in other words, you may still programatically expand and contract the dialog.

Utility Functions:

BOOL IsExpanded() const

Use this function to query the current expanded or contracted state of the dialog. TRUE denotes an expanded dialog, and FALSE denotes a contracted dialog.


BOOL Expand(BOOL bExpand)

Call this function to expand or contract the dialog manually. This function returns whether the new state matches the state passed in the "bExpand" parameter. In other words, TRUE on success, and FALSE on failure.

If the state passed into this function matches the current state of the dialog, this function returns TRUE immediately without calling any of the virtual notification functions OnDialogExpanding and OnDialogExpanded.

 

Overridables:

BOOL OnDialogExpanding(BOOL bExpand) const

This function is called when the dialog is about to expand or contract. The parameter "bExpand" tells whether we are about to expand or contract the dialog. Return TRUE to allow the operation to continue, and FALSE to keep the dialog from expanding or contracting.

The default implementation of this function simply returns TRUE to allow the dialog to change state.


void OnDialogExpanded(BOOL bExpand) const

This function is called after the dialog has expanded. The parameter "bExpand" tells whether the dialog is now in the expanded or contracted state. This is the appropriate place to do things like set the focus to a particular control, change text in controls on the dialog, etc.

The default implementation of this function does nothing.


Final notes:

If needed, is it possible to handle the message generated by the click of the "Advanced" button, simply by adding the appropriate message handler to your dialog's message map. Handling this message does not affect the operation of the expanding/contracting dialog. If you do choose to handle this message, be aware that this implementation of the expanding/contracting dialog guarantees that the dialog will have fully changed state before you receive the message from the Advanced button that it has been clicked. This is very useful, because you can assume that the dialog is (again) in a static state, with the appropriate controls enabled/disabled, before you receive the message.

Credits: the code in the ExpandBox() member function is based on code by Jeffery Richter in "Windows 95: A Developer's Guide"

Download demo project - 49 KB

Download source - 5 KB



Comments

  • Fantastic Job!

    Posted by Legacy on 06/18/2002 12:00am

    Originally posted by: Trent

    Thanks a lot, it's flawless!

    Well done!

    Reply
  • Better fix for your expand/contract sizing issue

    Posted by Legacy on 06/14/2002 12:00am

    Originally posted by: J.D. Bertron

    The earlier fix given by Shane Triem works only if you expand/contract vertically.
    You're better off keeping the original code the same and fix the size of the default box in the resource file.

    Just add 3 units to the dialog box's dimension and use the result for the default box dimension. (269 -> 271)

    IDD_MYDIALOG DIALOG DISCARDABLE 0, 0, 269, 202 ...

    CONTROL "",IDC_DEFAULT_BOX,"Static",SS_BLACKFRAME | NOT WS_VISIBLE,0,0,271,136 ...

    J.D.

    Reply
  • Howto easy create an app and my experience with size bug

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

    Originally posted by: Robin Holenweger

    Hi
    First of all: many thanks to Daniel G. Hyams for the great class!

    I'd like to tell here how it seems to be very easy to create an app an what experience I have with solving the size bug. I use Win2k SP2 with DevStudio 6.0 SP5

    The easiest way to create an app:
    - Use AppWizard to create a new dialog project (e.g. MyTest)
    - Add the CExpandingDialog class to the project
    - include "ExpandingDialog.h" in MyTestDlg.h
    - Replace all CDialog by CExpandingDialog the app's default dialog's files (e.g. in class CMyTestDlg, MyTestDlg.h and MyTestDlg.cpp) except the ones used in class CAboutDlg (which are in the same file)
    - complete the CMyTestDlg's constructor with ",IDC_DEFAULTBOX,IDC_ADVANCEDBUTTON,_T("Advanced >>"),_T("Advanced <<")" in MyTestDlg.cpp
    - Add a button/checkbox whatever with ID IDC_ADVANCEDBUTTON and a frame with ID IDC_DEFAULTBOX in the MyTestDlg dialog
    That's it (compile and start). This way you still have the icon, about box and so on in the title bar. Also Message Map and so on are derieved from CExpandingDialog.

    Size bug:
    The easiest way I found out is to exactly size the frame. Actually there are 3 points to take into account:
    - justify the bottom of the frame
    - justify the right edge of the frame
    - make the frame large enough so all controls are in it that should be enabled when contracted
    Note: It doesn't matter, where the top and left edges of the border are. Only make sure that the necessary controls are in it and if you want e.g. expanding down only, that in this case the right border edge is aligned with the border of the dialog (use ALT key to do this, see comment "No horizontal resizing problem here! (Read this)" of Chris Montgomery).

    Cheers

    Robin

    Reply
  • Expanding Dialog

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

    Originally posted by: Franz Klein

    Can't get the Dialog to expand to the left or upwards. Anybody got any ideas.

    Reply
  • Problems with reusing Dialog

    Posted by Legacy on 03/15/1999 12:00am

    Originally posted by: Roland Frank

    I had a problem with the CExpandingDialog class when I reuse
    
    a dialog.

    In some situations I want to do the following:

    CMyDialog dlg;

    dlg.DoModal();
    // do something with the dialog data
    dlg.DoModal();


    On the second call to DoModal() the dialog appears expanded, the frame
    that denotes the collapsed size is visible and the "expand button" does
    still have the text of the dialog template.

    as a quick an dirty solution I changed the OnInitDialog as follows:

    BOOL CExpandingDialog::OnInitDialog()
    {
    CDialog::OnInitDialog();
    m_bExpanded = TRUE; // CDialog::OnInitDialog leaves the dialog expanded!
    ExpandBox(FALSE);
    return TRUE;
    }

    The only problem here is, that if the user leaves the dialog expanded
    and it is then later shown again, it will be collapsed.
    For my current project this isn't a problem but it could be in later
    projects, so I'm still looking to solve this.

    Another little annoyance is, that during the collapse procedure all
    controlls get disabled and then later when the dialog expands again
    all controlls get enabled.
    In all but the most simplest dialog good user interaction means
    enabling and disabling dialog control depending on context.
    I solved this by packing all enabling/disabling logic into a single
    function I also call from OnDialogExpanded.

    Thanks for the class!
    Roland Frank

    Reply
  • keeping the horizontal edges the same as the dlg solution

    Posted by Legacy on 01/27/1999 12:00am

    Originally posted by: Shane Lim

    Well I have difficulty trying to align the static frame control
    size with the dlg edges when I want to edges of the dlg remaind
    the same when expanding and contracting. None of the methods
    suggested so far can effectively achieve this.

    here is my solution - even though it's involve a bit more coding.
    This method allow you to simply drop the static frame control
    and resizing it to inlcude the button with ID of IDC_ADVANCEDBUTTON.
    Of course you have to resizing the vertical size as before.

    in ExpandingDialog.h add
    public:
    void AlignToDlgHorzEdge(BOOL bAligned);

    private:
    BOOL m_bAligned;

    in ExpandingDialog.cpp add
    - initialize this in the constructor
    m_bAligned = FALSE;

    - add this method
    void CExpandingDialog::AlignToDlgHorzEdge(BOOL bAligned)
    {
    m_bAligned = bAligned;
    }

    - add/modified this section of code

    void CExpandingDialog::ExpandBox(BOOL fExpand)
    {
    ...
    if (m_bAligned) {
    _ASSERT(m_pSize != NULL);
    SetWindowPos(NULL,0,0,m_pSize->cx,rcDefaultBox.bottom - rcWnd.top,SWP_NOZORDER|SWP_NOMOVE);
    } else {
    // Note: this bit is the original code and I it seem to be fine to me
    // shrink the dialog box so that it encompasses everything from the top,
    // left up to and including the default box.
    SetWindowPos(NULL,0,0,
    rcDefaultBox.right - rcWnd.left,
    rcDefaultBox.bottom - rcWnd.top,
    SWP_NOZORDER|SWP_NOMOVE);
    }
    ...
    }

    in the MyDialog1.cpp in the contructor add
    AlignToDlgHorzEdge(TRUE);

    Bye 4 now !


    Shane L

    Reply
  • No horizontal resizing problem here! (Read this)

    Posted by Legacy on 01/16/1999 12:00am

    Originally posted by: Chris Montgomery

    Howdy,

    As for the dialog resizing bug problem - its not really a problem. I have used it in two different applications correctly. To get it to resize correctly just hold down alt and drag the image frame near both horizontal edges. It takes a couple tries to get it exactly right, but it works! No more horizontal dialog growth. ;^)

    - Chris Montgomery

    PS. - This is a great class. I like it.

    Reply
  • Fix for sizing bug when dialog expands

    Posted by Legacy on 12/21/1998 12:00am

    Originally posted by: Shane Triem

    After incorporating this class into my application, I found that the dialog width when contracted is
    different than when it is expanded due to a slight bug in the sizing algorithm.    
    
    (The downloadable sample demonstrates the bug) Below is the bug fix that should be made to the ExpandingDialog.cpp file:


    void CExpandingDialog::ExpandBox(BOOL fExpand)
    {
    //...

    // shrink the dialog box so that it encompasses everything from the top,
    // left up to and including the default box.
    // SetWindowPos(NULL,0,0,
    // rcDefaultBox.right - rcWnd.left,
    // rcDefaultBox.bottom - rcWnd.top,
    // SWP_NOZORDER|SWP_NOMOVE);

    // Modified by K. Shane Triem
    SetWindowPos(NULL,0,0,
    rcWnd.Width(),
    rcDefaultBox.bottom - rcWnd.top,
    SWP_NOZORDER|SWP_NOMOVE);
    //...
    }

    Reply
  • a quick way to expand/contract your Dialog Box

    Posted by Legacy on 10/09/1998 12:00am

    Originally posted by: nabin tewari

    You may use CWnd::MoveWindow function (remember that CDialog is derived
    
    from CWnd).
    Suppose CMyDialog is a dialog class derived from CDialog and there is a
    button labelled "advanced". The following code fragment shows how you
    can increase the height of your dialog class when the user clicks on the
    button. Contraction of the dialog box may be done in a similar fashion.

    void CMyDialog::OnClickAdvancedBtn()
    {
    //...
    CRect rect;
    GetWindowRect(&rect); // don't call GetClientRect
    rect.InflateRect(0,10);
    MoveWindow(&rect);
    //...
    }

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

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date