I solved my problem.
I had several problems inside of my OnPaint(), RecalcClientSize(), and RecalcLayout().
The problem was that I was storing the CDockContWnd's in a vector, as well as it's own CDockDialog children.
Inside of CDockContWnd::OnPaint() I was assuming that if it was in the vector it was a window. However I was only doing a delete in the vector and not actually removing it from the vector thus causing the pointer to become invalid, yet the vector size was unchanged. I fixed it by calling vector::clear() at the end of my delete cleanup loop. I also added several protection mechanisms in OnPaint() and any other layout/drawing function that required window or client rects.
I am re-designing this whole thing now to shutdown using destructors instead of destroy since I know how to fix the entire problem.
if (m_vChildren[i] && IsWindow(m_vChildren[i]->m_hWnd))
//Proceed with whatever we need to do
I also added a RecalcClientSize() function to CMainFrame. This takes into account all docked windows and resizes the client window based on what and where other windows are docked. Now each CDockContWnd has a dock state which tells me where it has been docked so I can compare coordinates like this:
As you can see it has a lot of debug messages in it which will be removed once I confirm all types of docking work correctly. I've also tested docking with a dock target - in other words docking more CDockDialog's inside of a CDockContWnd which calls CDockContWnd::RecalcLayout(). It works like a charm. Top and bottom docking has been disabled just so I can see if the left and right dock code works. Then I can use the left/right code as a template for the top/bottom code.
if (m_pView) m_pView->GetClientRect(&rect);
if (m_vDockWnds.empty()) return;
for (DWORD i=0;i<static_cast<DWORD>(m_vDockWnds.size());i++)
//text.Format(L"Dock: %d %d %d %d\n"
// L"Client: %d %d %d %d",
//Window is on right side of client
if (rectDock.left<=rect.right && mode==DLG_DOCK_RIGHT) rect.right=rectDock.left;
//Window is on far left side of client - use right edge for left side of client
if (rectDock.right>=rect.left && mode==DLG_DOCK_LEFT) rect.left=rectDock.right;
//Window is on bottom of client - use top edge for bottom of client
//if (rectDock.top>=rect.bottom && rectDock.bottom>=rect.bottom) rect.bottom=rectDock.top;
//Window is on top of client - use bottom edge for top of client
//if (rectDock.top<=rect.top && rectDock.bottom>=rect.top) rect.top=rectDock.bottom;
//text.Format(L"%d %d %d %d",rect.left,rect.top,rect.right,rect.bottom);
As you can see since I'm using a vector I can insert a window at any point. This means that as long as I provide some type of visual feedback and drag and drop tracking of windows, I can allow the user to drag any CDockDialog onto any CDockContWnd at any place. Based on coordinates then I can tell where in the vector I should insert the new window. This has a lot of flexibility and I can't wait to get it all working. Since the CRect class has a lot of functionality inside of it, testing where windows need to be placed will be quite simple.
for (DWORD i=0;i<m_vChildren.size();i++)
SWP_NOZORDER | SWP_SHOWWINDOW);
//Send a WM_PAINT to the child window so it can redraw controls and any child windows
//it may have
//Keep track of our current Y position in this container window
I wish MFC would have provided better docking support, but no worries, I'm implementing my own. Perhaps I'll implement some custom UI elements next.