Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A class for child dialogs

0.00/5 (No votes)
3 Nov 2003 5  
How to put a child dialog in a dialog box

Introduction

In an earlier article, I showed how interacting controls in a dialog box can be encapsulated in a custom control that handles all the interactions between the controls. An even better way to handle interacting controls is to put them into a child dialog. This article presents a simple class that helps you to put a child dialog into a dialog box.

Background

I was developing a MFC application to play MIDI files, and one of the dialog boxes seemed to end up being much too complicated. It was handling the interactions between an array of volume controls, one per track, and a parallel array of buttons for muting the tracks. I decided it would be better to have some sort of object containing the volume control slider and the mute button for a single track, any interactions between them being handled within the object, and the dialog box could then have an array of such objects.

In C terms, instead of having a structure containing two parallel arrays of objects, I wanted an array of structures, each structure containing two related objects.

A web search for advice on how to do this with controls, yielded information on custom controls, so my first attempt to handle the interacting controls used a custom control and is described here. Then I sorted out a better way of doing it, which is to put the interacting controls into a child dialog box, and this article describes how to do that.

Example program

The example program accompanying this article takes part of the standard Color selection dialog box, and handles the interaction between the Hue, Saturation, Luminosity, Red, Green and Blue controls by putting them in a child dialog. The HSL values affect the RGB values, and vice versa, but the parent dialog box doesn't have to know anything about this, it just interacts with the child dialog in terms of RGB values. The child dialog also shows the current color.

Creating a Child Dialog

The steps required to create a child dialog that will appear in a parent dialog box are as follows:

  • Use Dialog Editor and ClassWizard in the normal way to create the child dialog box and its associated class (called ChildDialog (say), and based on CDialog), also to lay out the controls in the child dialog box and create associated member variables in the class. But note the following:
    • Change the properties of the child dialog box (by right-clicking on its border, then choosing Properties) as follows:
      • Style: Child
      • Border: None
      • More styles: Visible and Control must be ticked
    • Delete the OK and Cancel buttons from the child dialog box (otherwise the child dialog will disappear from its parent if you click them).
    • Drag the guidelines to the edge of the box and place the controls right up to the edge (otherwise the child dialog will have a gap all round it).

  • Add any code and data to ChildDialog to make the child dialog box work as required, including any interactions required between the controls (that's the whole point of putting the controls into a child dialog).
  • In ChildDialog.h, #include "GenericChildDialog.h" and change the derivation of ChildDialog from CDialog to CRHGenericChildDialog. Replace the standard constructor for ChildDialog with one without parameters. (It's not essential to do this, but I found it makes other things much easier.) Add a declaration of CRHGetDialogID().
    #include "GenericChildDialog.h"
    
    ...
    // changed from CDialog
    
    class ChildDialog : public CRHGenericChildDialog   
    {
    // Construction
    
    public:
        //ChildDialog(CWnd* pParent = NULL); 
    
                      // standard constructor - removed
    
        ChildDialog();                       // added
    
        virtual int CRHGetDialogID();        // added
    
        ...
  • In ChildDialog.c, replace the standard constructor for ChildDialog with the new constructor (and move the AFX_DATA_INIT lines to the new constructor, so that ClassWizard puts any initialization it wants in the right place) and add the CRHGetDialogID() routine:
    //ChildDialog::ChildDialog(CWnd* pParent /*=NULL*/)
    
    //    : CDialog(ChildDialog::IDD, pParent)
    
    //{
    
    //}
    
    // ^^^ standard constructor replaced by
    
    // constructor without parameters vvv
    
    ChildDialog::ChildDialog()
    {
        // vvv AFX_DATA_INIT stuff moved from standard constructor
    
        //{{AFX_DATA_INIT(ChildDialog)
    
        ... whatever ClassWizard might have put in ...
        //}}AFX_DATA_INIT
    
    }
    
    int ChildDialog::CRHGetDialogID()
    {
        return(IDD);
    }

Putting a Child Dialog into a parent dialog box

To put the child dialog into a parent dialog box:

  • Use Dialog Editor and ClassWizard in the normal way to create the parent dialog box and its associated class (called ParentDialog (say), and based on CDialog).
  • Use Dialog Editor in the normal way to place a Group Box control in the parent dialog box at the place where you want the child dialog to appear. Only the position of the top left corner of this control is used. Its size is not critical, because it will be resized by CRHGenericChildDialog to match the size of the child dialog. Give this control the ID IDC_CHILDPLACEMARKER (say).

  • Use ClassWizard in the normal way to add a ChildDialog data member (called vChildDialog, say) to ParentDialog.
  • If necessary, create ParentDialog::OnInitDialog() (in ClassView, right-click on ParentDialog, choose "Add Windows Message Handler ...", select WM_INITDIALOG, click "Add and Edit").
  • In ParentDialog::OnInitDialog(), call
    vChildDialog.CRHCreateGenericChildDialog(this, IDC_CHILDPLACEMARKER, 
        Id, &BorderRectangle);

    Id is a WPARAM value that will appear in wparam in any message sent by the child dialog up to the parent dialog box, thereby allowing the parent dialog box to identify where the message came from (this is useful if you have more than one child dialog of the same sort in the parent dialog box). BorderRectangle is a CRect you can use to specify the size of the gap between the child dialog and the Group Box control in the parent dialog box. CRect(4,15,4,6) looks quite good, and is available as CRHGenericChildDialog::CRHRectForGroupBox. You can use different borders, or use NULL if you want the child dialog to appear in place of the Group Box control without any gap round it.

Trying out the Child Dialog

You just need to create the parent dialog box, and the child dialog should now appear in it. If you haven't yet written some code to create the parent dialog box, just add a NewParent item to your main menu, and use ClassWizard in the normal way (using the "Message Maps" tab) to add a COMMAND handler for it. Then create the parent dialog box as a modal dialog box:

void CCc9App::OnNewparent() 
{
    // TODO: Add your command handler code here

    
    ParentDialog vParentDialog;
    vParentDialog.DoModal();
}

Communication between a child dialog and its parent dialog box

To finish off the child dialog, we have to make it communicate with its parent dialog box. The child dialog must tell its parent dialog box when something has happened to the child dialog that the parent needs to know about. And the parent dialog box must be able to tell the child dialog to do various things (exactly what depends on just what functionality the child dialog supports).

This is very similar to how a Windows common control communicates with its parent dialog box:

  • A control informs its parent of any changes by sending a message, which is handled in the class for the parent dialog box by a message handler routine (e.g. OnHScroll()).
  • The parent tells the control what to do by calling a member function (e.g. CSliderCtrl::SetPos()).

So our child dialog and its parent dialog box communicate similarly, as follows:

  • The child dialog informs its parent of any changes by sending a message (by calling CRHGenericChildDialog::CRHPostMessageToParent()), which is handled in the class for the parent dialog box by a message handler routine (e.g. OnCHANGED_RGB()).
  • The parent tells the child dialog what to do by calling a member function (e.g. ChildDialog::SetRGB()).

Using the Child Dialog

Now that we've created a child dialog that handles the interactions between its controls internally, and that communicates with its parent dialog box, we can put one into a dialog box using a placemarker control

void CRHGenericChildDialog::CRHCreateGenericChildDialog(CWnd *pParent, 
                                               int PlaceMarkerCtrlID, 
                                               int Id, 
                                               CRect *ABorderRect)

or we can put an array of them into a dialog box using a CRect.

void CRHGenericChildDialog::CRHCreateGenericChildDialog(CWnd *pParent, 
                                               CRect *pPlaceMarkerRect, 
                                               int Id, 
                                               CRect *ABorderRect)

The interactions between the HSL controls, the RGB controls and the colored squares are handled entirely within the child dialogs. In this last example, the main dialog box knows only about an array of 5 objects whose interface is just in terms of RGB. This approach is much simpler than having the main dialog box handle the interactions between 65 separate controls.

Finally

If anyone's interested in seeing the original MidiPlay application that prompted this article, you can find it here (click on the "MidiPlay.exe v1.06" link). For a version of Midiplay using custom controls as described in my earlier article, click on the "MidiPlay.exe v1.07" link. For a version of Midiplay using child dialogs as described in this article, click on the "MidiPlay.exe v1.08" link.

History

  • 19th October 2003 - first submitted.
  • 1st November 2003 - major bug fixed (was leaking GDI resources). See Joseph M. Newcomer's article to see the SelectObject() mistake that I made. I originally saw the article on Joe's excellent site (thanks very much Joe!).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here