1

I have a MFC project I maintain, that recently got upgraded from VC6->2013, and apparently some core Windows code become stricter with regards WS_POPUP and WS_CHILD, can no longer be mixed. I have a program that crates a child dialog to alter some settings (of an image for example), the child dialog would have top level focus, and this child dialog would also spin off 2 new CWnd of it's own a essentially preview and result window, with the following dwStyle flags set WS_POPUP | WS_CAPTION | WS_CHILD which worked in VC6, but now is a no no. The full window create logic,

m_wndDst = new CWnd;
m_wndDst->CreateEx(WS_EX_CONTROLPARENT , NULL,
                             szTitle, WS_POPUP | WS_CHILD | WS_BORDER | WS_CAPTION, 
                             10, 10, 10, 10, m_hWnd, NULL, NULL);

This child dialog will often destroy and re-create these child CWnds it's perhaps not the best solution, but it's a fairly large legacy project, that I wish to disturbe the least amount as possible.

So I remove the WS_POPUP per another S.O QA, but that isn't quite right either, because now my CWnds are now being renderws within my initial child dialog,clipped and have z-order issues with regards to controls, this is no good, even if it makes sense, If I change the hWndParent param in CrateEx to the top level Parent hWnd, this is closer to what I want, but my initial child dialog still has focus, and the contents of my 2 sub windows does not get rendered (essentially transparent), the title is there, the border is there, but no content, and the CWnds can not be selected and freely moving around.

I've tried removing the WS_CHILD style, and leaving the WS_POPUP, but that is no good either, as in wincore.cpp the PreCreateWindow does a check to ensure that it is a child window,

    if (cs.lpszClass == NULL)
    {
        // make sure the default window class is registered
        VERIFY(AfxDeferRegisterClass(AFX_WND_REG));

        // no WNDCLASS provided - use child window default
        ASSERT(cs.style & WS_CHILD);
        cs.lpszClass = _afxWnd;
    }

So in essence what I am trying to achieve is, I need my initial child dialog to be able to spin up to CWnds that act more like popup windows, titlebar, and a border, and really nothing else. They has need to be at the same focus level as the child dialog that created them (the main frame is untouchable until the child dialog is closed), if the child dialog closes, it would close the 2 CWnds as well, and like before should be able to move out side of the main frame.

Any help, articles, suggestions would be greatly appreciated.

EDIT 1 based on comments --- this gets me close really close, just have re-draw issues.

//auto dwStyle = WS_POPUP | WS_CHILD | WS_BORDER | WS_CAPTION; // original
    auto dwStyle = WS_BORDER | WS_CAPTION | WS_VISIBLE;
    //auto dwStyle = WS_CHILD | WS_BORDER | WS_CAPTION | WS_VISIBLE;

    CString sClass = AfxRegisterWndClass(
            CS_VREDRAW | CS_HREDRAW,
            ::LoadCursor(nullptr, IDC_ARROW),
            static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)),
            ::LoadIcon(nullptr, IDI_APPLICATION));

    auto result = pWndSrcOrDest->CreateEx(WS_EX_CONTROLPARENT, sClass,
        sWindowTitle, dwStyle, 10, 10, 10, 10, this->m_hWnd, nullptr, nullptr);
andrew.rockwell
  • 621
  • 7
  • 18
nagates
  • 620
  • 13
  • 40
  • 1
    I also think that you need to remove WS_CHILD and provide a valid predefined window class in CreateEx according to the documentation in CWnd::CreateEx: Use the AfxRegisterWndClass function to register window classes. User defined window classes are available in the module where they are registered. You also need to set the hwndParent to first modal dialog when you create the second level dialogs. This way the modal dialog will be the "owner" of the dialogs it creates and they should behave the way you need to. – VuVirt Sep 28 '16 at 16:28
  • Yea, I guess I don't really understand what these window classes are? – nagates Sep 28 '16 at 16:48
  • There are predefined windows classes like Static, Edit, etc. In your case you need to register a custom class to use as a second parameter of the CreateEx function. So you need to provide a class name for the CreateEx call for the first dialog and another class name for the CreateEx calls for the second level dialogs. You can achieve this by calling AfxRegisterWndClass(NULL) and use the returned class name in CreateEx. – VuVirt Sep 28 '16 at 16:51
  • Check The AfxRegisterWndClass Function in: https://msdn.microsoft.com/en-us/library/a77269ff.aspx – VuVirt Sep 28 '16 at 16:56
  • Also here: http://stackoverflow.com/questions/15807725/how-create-derived-from-cwnd-pop-up-window – VuVirt Sep 28 '16 at 16:59
  • *"child dialog"* - wrong terminology, and it **does** matter here. Dialogs are commonly owned, not children (see [Window Features](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632599.aspx) for details). Plus, `WS_POPUP` and `WS_CHILD` have always been mutually exclusive [window styles](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600.aspx). Nothing changed there. – IInspectable Sep 28 '16 at 17:38
  • They may have always been mutually exclusive, but it apparently was not strictly enforced, because I've seen the old compiled version work. – nagates Sep 28 '16 at 17:41
  • @VuVirt this gets me really close, some re-draw issues, //auto dwStyle = WS_POPUP | WS_CHILD | WS_BORDER | WS_CAPTION; // original auto dwStyle = WS_BORDER | WS_CAPTION | WS_VISIBLE; CString sClass = AfxRegisterWndClass( CS_VREDRAW | CS_HREDRAW, ::LoadCursor(nullptr, IDC_ARROW), static_cast(::GetStockObject(WHITE_BRUSH)), ::LoadIcon(nullptr, IDI_APPLICATION)); auto result = pWndSrcOrDest->CreateEx(WS_EX_CONTROLPARENT, sClass, sWindowTitle, dwStyle, 10, 10, 10, 10, this->m_hWnd, nullptr, nullptr); – nagates Sep 28 '16 at 17:53
  • Yes this looks good to me. You can try to add ws_clipsiblings and ws_clipchildren to windows styles to see if the flicker is gone. – VuVirt Sep 28 '16 at 18:04
  • Oh wait you also need to keep ws_popup style. Only ws_child needs to be removed. – VuVirt Sep 28 '16 at 18:06
  • Try to remove cs_hredraw and cs_vredraw too. Replace then with 0 or cs_dblclick. This should fix flickering. – VuVirt Sep 28 '16 at 18:08
  • getting much closer – nagates Sep 28 '16 at 19:08
  • @nagates - I added an answer to clear the things up and so that you can accept it when you make it work – VuVirt Sep 29 '16 at 05:53
  • Thanks for doing that, I was going ask you do that as well, still having some issues with content flickering, probably related to things being down a little different than before, but should be able to figure it out. Will accept answer, when done. Thanks, again! – nagates Sep 29 '16 at 15:57

1 Answers1

2

You can do it like this:

LPCTSTR strClassNameDlgOwner = AfxRegisterWndClass(NULL);

pWndOwner->CreateEx(WS_EX_CONTROLPARENT, strClassNameDlgOwner,
    sWindowTitleOwner, WS_POPUP | WS_BORDER | WS_CAPTION | WS_VISIBLE, 10, 10, 10, 10, this->m_hWnd, nullptr, nullptr); // this->m_hWnd is the handle to the Main application window

LPCTSTR strClassNameDlgOwned = AfxRegisterWndClass(NULL);

pWndOwned->CreateEx(WS_EX_CONTROLPARENT, strClassNameDlgOwned,
    sWindowTitleOwned, WS_POPUP | WS_BORDER | WS_CAPTION | WS_VISIBLE, 10, 10, 10, 10, pWndOwner->GetSafeHwnd(), nullptr, nullptr);

The first dialog is the Owner window, while the second level dialogs that the first one creates are the Owned windows.

VuVirt
  • 1,887
  • 11
  • 13