SysTutorials

Embedding Child Dialogs in MFC Parent Dialogs

· updated April 11, 2026

Embedding child dialogs within a parent dialog is a practical technique for organizing complex MFC applications. This approach modularizes your UI by breaking it into logical, reusable components rather than cramming everything into a single dialog resource.

Basic Implementation

Create the child dialog as a WS_CHILD window and position it within the parent’s client area. The key difference from standalone dialogs is the window style and parent specification.

class MyParentDlg : public CDialogEx
{
    // ...
private:
    MyChildDlg m_childDlg;
};

BOOL MyParentDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Create child dialog with this dialog as parent
    m_childDlg.Create(IDD_CHILD_DIALOG, this);

    // Position and size the child dialog
    CRect rc(150, 0, 350, 200);
    m_childDlg.MoveWindow(rc);
    m_childDlg.ShowWindow(SW_SHOW);

    return TRUE;
}

Always declare the embedded dialog as a member variable of the parent class rather than as a static variable. Member variables provide proper lifetime management, automatic cleanup, and better integration with the parent’s message flow.

Dialog Resource Configuration

Configure your embedded dialog’s resource properties in the resource editor:

These settings ensure the child dialog appears as a seamless component within the parent window without its own frame, title bar, or system menu decorations.

Dynamic and Responsive Positioning

For layouts that adapt to parent window size, calculate child positions dynamically:

BOOL MyParentDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    m_childDlg.Create(IDD_CHILD_DIALOG, this);

    // Position based on parent dimensions
    CRect parentRect;
    GetClientRect(&parentRect);

    CRect childRect;
    childRect.left = parentRect.Width() / 4;
    childRect.top = parentRect.Height() / 10;
    childRect.right = childRect.left + 200;
    childRect.bottom = childRect.top + 150;

    m_childDlg.MoveWindow(childRect);
    m_childDlg.ShowWindow(SW_SHOW);

    return TRUE;
}

Handling Resize Events

For responsive layouts that reposition child dialogs when the parent is resized, override WM_SIZE:

void MyParentDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);

    if (m_childDlg.GetSafeHwnd())
    {
        CRect parentRect;
        GetClientRect(&parentRect);

        // Recalculate child position based on new parent size
        CRect childRect;
        childRect.left = parentRect.Width() / 4;
        childRect.top = parentRect.Height() / 10;
        childRect.right = childRect.left + 200;
        childRect.bottom = childRect.top + 150;

        m_childDlg.MoveWindow(childRect);
    }
}

Always check that the child dialog’s window handle is valid with GetSafeHwnd() before attempting to reposition it. This guard prevents crashes if the child hasn’t been created yet or has been destroyed prematurely.

Message Handling and Communication

Child dialogs receive and process their own messages independently. Handle WM_INITDIALOG, WM_COMMAND, and other messages in the child dialog’s message map as you would for any standalone dialog.

Parent-to-Child Communication

Call member functions on the child dialog directly:

// In parent dialog
m_childDlg.UpdateData(TRUE);  // Collect child's dialog data
m_childDlg.SetWindowText(_T("New Title"));

// Create public methods in child for parent access
m_childDlg.RefreshContent();

Child-to-Parent Communication

Store a pointer to the parent or use custom Windows messages:

// In child dialog class
class MyChildDlg : public CDialogEx
{
private:
    MyParentDlg* m_pParent;

public:
    void SetParent(MyParentDlg* pParent) { m_pParent = pParent; }
};

// In parent's OnInitDialog
m_childDlg.Create(IDD_CHILD_DIALOG, this);
m_childDlg.SetParent(this);

Alternatively, send custom messages from child to parent using SendMessage() on the parent window handle:

// In child dialog
::SendMessage(GetParent()->GetSafeHwnd(), WM_MY_CUSTOM_MESSAGE, wParam, lParam);

Multiple Child Dialogs

For complex UIs with several embedded dialogs, declare multiple member variables and initialize each in OnInitDialog():

class MyParentDlg : public CDialogEx
{
private:
    MyChildDlg m_childDlg1;
    MyChildDlg m_childDlg2;
    MyChildDlg m_childDlg3;
};

BOOL MyParentDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Create and position first child
    m_childDlg1.Create(IDD_CHILD_DIALOG, this);
    m_childDlg1.MoveWindow(CRect(0, 0, 200, 150));
    m_childDlg1.ShowWindow(SW_SHOW);

    // Create and position second child
    m_childDlg2.Create(IDD_CHILD_DIALOG, this);
    m_childDlg2.MoveWindow(CRect(210, 0, 410, 150));
    m_childDlg2.ShowWindow(SW_SHOW);

    // Create and position third child
    m_childDlg3.Create(IDD_CHILD_DIALOG, this);
    m_childDlg3.MoveWindow(CRect(0, 160, 410, 250));
    m_childDlg3.ShowWindow(SW_SHOW);

    return TRUE;
}

To avoid hardcoding positions, use a helper function for consistent spacing:

void PositionChildDlg(CDialogEx& child, int x, int y, int width, int height)
{
    child.Create(IDD_CHILD_DIALOG, GetParent());
    child.MoveWindow(x, y, width, height);
    child.ShowWindow(SW_SHOW);
}

Cleanup

When using member variables (the recommended approach), the child dialog is automatically destroyed when the parent dialog is destroyed. The MFC framework calls DestroyWindow() on all child windows during parent cleanup, so explicit cleanup code is rarely necessary.

If you need explicit control, override OnDestroy() in the parent:

void MyParentDlg::OnDestroy()
{
    if (m_childDlg.GetSafeHwnd())
    {
        m_childDlg.DestroyWindow();
    }

    CDialogEx::OnDestroy();
}

Common Pitfalls

Creating child dialogs as stack-allocated locals: Don’t create child dialogs on the stack in OnInitDialog() — they’ll be destroyed when the function exits. Always use member variables.

Forgetting to check GetSafeHwnd(): Before calling any methods on a child dialog after creation, verify the window exists with GetSafeHwnd() to avoid null pointer dereferences.

Not handling WM_SIZE during window resizing: If your parent dialog is resizable, child dialogs won’t automatically adjust. Handle WM_SIZE to maintain responsive layouts.

This pattern works well for creating tabbed interfaces, wizard-style layouts, property sheets with custom configurations, or splitting complex forms into manageable, reusable sections.

← All posts