If the ulterior motive for SetParent
is to keep the dialog in front of the view then the following might work (or it might not, depending on UI specifics). If, however, the reason is to clip the dialog to the parent view, then this won't help, though it still attempts to explain why.
Main issue here is an inconsistency in how Windows applies visual styles to (descendents of) child windows, or rather does not do that. The issue is not confined to VC++ or MFC, and has been noted [1], [2], [3], [4] (the last link is an open case on dotnet/winforms but also references MFC). Because of this issue, re-parenting the dialog as a child of the view "loses" the theming.
One additional complication here is that the window in question is a dialog, and dialogs are owned windows. Just calling SetParent
changes the parent of the dialog, but leaves the owner in place. This goes against the rule that A window can have a parent or an owner but not both. Also, SetParent
requires that the style bits be updated to remove WS_POPUP
and add WS_CHILD
instead. However, neither clearing the owner nor fixing the style bits changes the styling behavior once the dialog is made a child of the view.
The alternative is to change the owner of the dialog, instead of its parent, in which case the visual styles do in fact get applied. This keeps the dialog in front of its owner in the Z order, though it does not clip it to the owner area. The caveat, however, is that the owner must be a popup or overlapped window, so it cannot be set to the view itself, which is a child window, but instead to its closest popup/overlapped ancestor. In UIs with a single top-level window, this often means the one and only main window.
Sample code is below, with some comments inlined, and the #if 0
part that's not working.
void CTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// dialog gets created with parent = desktop window
// owner = app main window
m_wndTestDlg.Create(IDD_ABOUTBOX, this);
// visual styles *are* applied at this point
m_wndTestDlg.ShowWindow(SW_SHOW);
}
void CTestView::OnDialog1()
{
if (m_wndTestDlg.IsWindowVisible())
{ m_wndTestDlg.ShowWindow(SW_HIDE); }
else
{
#if 0 // visual styles *not* applied to test dialog
// the following make re-parenting consistent with the rules
// but visual styles are still not applied
// ::SetWindowLongPtr(m_wndTestDlg.m_hWnd, GWLP_HWNDPARENT, NULL);
// m_wndTestDlg.ModifyStyle(WS_POPUP | WS_CHILD, WS_CHILD);
m_wndTestDlg.SetParent(this);
#else // visual styles *are* applied to test dialog
// for an owned window, the following sets the owner, not the parent
// the new owner must be ws_popup or ws_overlapped, thus ga_root
::SetWindowLongPtr(m_wndTestDlg.m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)::GetAncestor(m_hWnd, GA_ROOT));
#endif
m_wndTestDlg.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
}
}