1

I'm trying to do exactly the same as the article describes here: "C++ MFC Feature Pack --> Create multiple CDockablePanes onto an CDialog"

I followed his procedure and now am able to undock and move the CDockablePane, but get the same crash when dock it back. In his own answer, he said he created the dummywnd by himself so MFC skipped the creation and the call to GetTopLevelFrame(). And this is where I got confused, how do I create the dummywnd exactly?

My second question is how do I exchange data between the CMyFrame and CDialog?

The person who asked and answered the question seems to be inactive for years and unreachable. Could anyone please help or have any ideas?

Thanks,


Edit:

I break the program and traced back exactly as the other author described. The dummy window mentioned above is actually in afxdragframeimpl.cpp:

void CMFCDragFrameImpl::MoveDragFrame(BOOL bForceMove)

where it creates:

m_pWndDummy = new CDummyDockablePane;

and calls:

m_pWndDummy->CreateEx(0, _T(""), AFXGetTopLevelFrame(m_pDraggedWnd), CRect(0, 0, 0, 0), FALSE, AFX_DUMMY_WND_ID, WS_CHILD);

And yes, I'm trying to create a CFrameWndEx as child window in my dialog, and then add child CDockablePane in that CFrameWndEx.

Basically I have a MFC dialogA with some controls, and within this dialogA I need some tear-off tabsXYZ, and I need to add some controls within each of the tear-off tabsXYZ. So this means each tear-off tabsXYZ is actually one child dialogB. So this comes to where I try to use CDockablePanes (CPaneDialog actually) within the dialogA.

Community
  • 1
  • 1
Yifeng
  • 11
  • 4
  • I don't have much experience with this, but the link you posted looks like a hack. There is no "dummy window" in there. It seems to create a `CFrameWndEx` as child window of a dialog box. I don't think that's advisable. Do you have a dialog based application? If so, are you trying to add panes in to main dialog? Note that you can just create a `CFrameWndEx` and add child dialog to it, or add child dialog in to docable pane. You can skip the doc/view part. – Barmak Shemirani Aug 17 '16 at 05:45
  • Thanks for your response Barmak. It is not a hack, I have changed how the link shows. And added some more details on top below 'Edit' section. Do you have any advice on how to use child tear-off/dockable dialogs in a dialog? Best. – Yifeng Aug 17 '16 at 15:07
  • You didn't answer my question. There is no reason to create a frame window as child window of a dialog, there is nothing to gain. `CFrameWndEx` can do everything `CDialog` can, and more. You can add child dialog to frame window. The method mentioned in the other link may seem more convenient to beginners, but it requires awful hacking which causes more problems down the line. – Barmak Shemirani Aug 17 '16 at 17:18
  • I have a relatively large and complicated application and it is CMDIFrameWnd based. The part where I am working on is a CDialogEx (say dialogA) and I'm only able to get the data I need from the class associated with that dialogA. And I need to create child dialogXYZ in dialogA to modify some property. I could have used a tab control and each tab corresponds one of dialog X,Y and Z. But in the mean time, I need dialog X,Y and Z to appear all at once on screen, so I was trying to find a "tear-off tabs" solution. I didn't get any reference on "tear-off tabs in CDialogEx" so it brings where I am. – Yifeng Aug 17 '16 at 19:55

1 Answers1

0
BOOL CMyDlg::OnInitDialog()  
{      
    CRect wndRect;  
    GetWindowRect(wndRect);    
    m_pFrame = new CMyFrame();  
    m_pFrame->Create(NULL, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, wndRect, this);  
    m_pFrame->MoveWindow(wndRect);

    CDialog::OnInitDialog();
    ...
}

I don't recommend the above code where frame window is placed in a dialog, because CFrameWndEx does all sorts of strange things, it's easy to break this code. Surprisingly, it works fine on VS2015, I couldn't duplicate any crash. But the window's behavior is still odd.

It's better to make a new frame window, and place a child dialog in frame. For example:

class CMyFrame : public CFrameWndEx
{
    CDialog m_dialog;

    int OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        CFrameWndEx::OnCreate(lpCreateStruct);
        m_dialog.Create(IDD_CHILD1, this);
        CRect rc;
        m_dialog.GetClientRect(&rc);
        m_dialog.SetWindowPos(NULL, 0, 0, rc.right, rc.bottom, SWP_SHOWWINDOW);
        return 1;
    }

    DECLARE_MESSAGE_MAP()
};

You open the window as follows:

void CMyMainFrame::OnButton()
{
    CMyFrame *frame = new CMyFrame;
    frame->LoadFrame(IDR_MAINFRAME, 
            WS_POPUPWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU, this);
    frame->SetMenu(0);
    frame->ShowWindow(SW_SHOW);
}

You can also create a child dialog and put that inside a docking pane. For example:

class CMyFrame : public CFrameWndEx
{
    CDockablePane m_DockWnd;
    CDialog m_dialog;

    int OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        CFrameWndEx::OnCreate(lpCreateStruct);

        CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
        CDockingManager::SetDockingMode(DT_SMART);
        EnableAutoHidePanes(CBRS_ALIGN_ANY);

        m_DockWnd.Create(_T("Test"), this, CRect(0, 0, 200, 200), TRUE, 0,
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
            CBRS_LEFT | CBRS_FLOAT_MULTI);

        m_dialog.Create(IDD_PAGE1, &m_DockWnd);
        CRect rdialog;
        m_dialog.GetClientRect(&rdialog);
        m_dialog.SetWindowPos(NULL, 0, 0, rdialog.Width(), rdialog.Height(), SWP_SHOWWINDOW);

        m_DockWnd.SetMinSize(rdialog.Size());
        m_DockWnd.EnableDocking(CBRS_ALIGN_ANY);

        EnableDocking(CBRS_ALIGN_ANY);

        DockPane(&m_DockWnd);

        return 0;
    }
    ...
};

Also remember, if your main window is also CFrameWndEx then you make something like the following calls in InitInstance:

SetRegistryKey(_T("MyCompany\\MyApp"));
SetRegistryBase(_T("MainFrame"));

When you open a new frame window you must change the registry base with

SetRegistryBase(_T("CMyFrame"));

Then change it back to SetRegistryBase(_T("MainFrame")) when you exit CMyFrame

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • Thank you very much Barmak, this is a very detailed answer. I haven't get a chance to try it yet, but it looks like it could work very well. Best. – Yifeng Aug 22 '16 at 14:31