Embedding Child Dialogs in MFC Parent Dialogs
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:
- Style: Set to “Child” (not Popup or Overlapped)
- Border: Set to “None”
- System Menu: Unchecked
- Title Bar: Unchecked
- Control Box: Unchecked
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.
