CDialog:为什么 SetParent(this) 改变视觉风格?

CDialog: Why SetParent(this) change the visual Style?

我在 CView 派生的 class m_wndTestDlg.Create(CTestDlg::IDD, this); 和 Show/Hide 中创建了一个非模态 CDialog 并使用以下代码移动对话框

h 文件:

class CTestView : public CView
{
    :
    CTestDlg m_wndTestDlg;
    :
}

cpp 文件:

void CTestView::OnInitialUpdate()
{
    CView::OnInitialUpdate();
    m_wndTestDlg.Create(CTestDlg::IDD, this);
}

void CTestView::OnDialog1()
{
    BOOL b = m_wndTestDlg.IsWindowVisible();
    if (b)
      m_wndTestDlg.ShowWindow(SW_HIDE);
    else {
      m_wndTestDlg.ShowWindow(SW_SHOW);
   // if (!m_wndTestDlg.IsWindowVisible()) {    // still not viewable, outside ParentScreen, move to top of CView
        m_wndTestDlg.SetParent(this);
        m_wndTestDlg.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
    }
}

我不明白为什么 SetParent 将视觉风格更改为旧的 WinXP 风格?

如果 SetParent 别有用心的目的是将对话框保持在视图前面,那么以下方法可能有效(也可能无效,具体取决于 UI 具体情况)。但是,如果原因是将对话框剪辑到 parent 视图,那么这将无济于事,尽管它仍然试图解释原因。

这里的主要问题是 Windows 如何将视觉样式应用于 child windows(的后代),或者更确切地说 不是 去做。该问题并不局限于 VC++ 或 MFC,并且已被注意到 [1], [2], [3], [4] (the last link is an open case on dotnet/winforms but also references MFC)。由于这个问题,re-parenting 作为视图 child 的对话框“丢失”了主题。

这里的另一个复杂问题是所讨论的 window 是一个对话框,而对话框是 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 需要更新样式位以删除 WS_POPUP 并添加 WS_CHILD 代替。但是,一旦对话框成为视图的 child,清除所有者和修复样式位都不会改变样式行为。

另一种方法是更改​​对话框的 owner,而不是其 parent,在这种情况下,视觉样式实际上会得到应用。这使对话框在 Z 顺序中保持在其所有者前面,但不会将其剪辑到所有者区域。但是,需要注意的是,所有者必须是弹出窗口或重叠 window,因此不能将其设置为视图本身,即 child window,而是与其最接近的popup/overlapped 祖先。在带有单个 top-level window 的 UI 中,这通常意味着唯一的主要 window.

示例代码如下,内联了一些注释,#if 0 部分 工作。

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);
    }
}