Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
100 views
in Technique[技术] by (71.8m points)

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

without SetParent with SetParant(this)

I create a Non modal CDialog in a CView derived class m_wndTestDlg.Create(CTestDlg::IDD, this); and Show/Hide and move the Dialog with the following code

h File:

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

cpp File:

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

I don't understand why SetParent change the visual style to the old WinXP style?

question from:https://stackoverflow.com/questions/65907591/cdialog-why-setparentthis-change-the-visual-style

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...