將窗格停靠到子框架
有時,應用程式必須具有不與主框架對接而是與子框架對接的窗格。通常它是 MDI 應用程式。在 MFC Feature Pack 中,這樣的子幀是從 CMDIChildWndEx
類繼承的,並且作為主框架(繼承自 CMDIFrameWndEx
)具有此類對接所需的所有程式碼。
但是兒童框架有一些技巧。這個例子展示了它們。
// Declare child frame
class CChildFrame : public CMDIChildWndEx
{
DECLARE_DYNCREATE(CChildFrame)
protected:
// declare our pane
CDockablePane m_wndPane;
public:
CChildFrame();
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// CMDIChildWndEx class haven't serialization for state of the docking manager,
// so we need to realize it manually.
//
// Docking state serialization methods:
virtual void SaveBarState(LPCTSTR lpszProfileName) const;
virtual void LoadBarState(LPCTSTR lpszProfileName);
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()
};
// CChildFrame Implementation
IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWndEx)
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWndEx)
ON_WM_CREATE()
ON_WM_DESTROY()
END_MESSAGE_MAP()
CChildFrame::CChildFrame()
{
// Trick#1: Add this line for enable floating toolbars
m_bEnableFloatingBars = TRUE;
}
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CMDIChildWndEx::PreCreateWindow(cs) )
return FALSE;
cs.style = WS_CHILD | WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
| FWS_ADDTOTITLE | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_MAXIMIZE;
// Trick#2: Add this line for remove the ugly client edge of the child frame.
cs.dwExStyle &= (~WS_EX_CLIENTEDGE);
return TRUE;
}
void CChildFrame::SaveBarState(LPCTSTR lpszProfileName) const
{
const_cast<CChildFrame*>(this)->GetDockingManager()->SaveState(lpszProfileName);
// Trick#3: we need to call serialization method of CMFCToolBar panes that may be docked to the child frame.
CObList list;
const_cast<CChildFrame*>(this)->GetDockingManager()->GetPaneList(list, FALSE, NULL, FALSE);
if (list.GetCount() > 0) {
POSITION pos = list.GetTailPosition();
while (pos != NULL) {
CMFCToolBar* pToolBar = DYNAMIC_DOWNCAST(CMFCToolBar, list.GetPrev(pos));
if (pToolBar != nullptr) {
pToolBar->SaveState(lpszProfileName);
}
}
}
}
void CChildFrame::LoadBarState(LPCTSTR lpszProfileName)
{
// Trick#3: we need to call serialization method of CMFCToolBar panes that may be docked to the child frame.
CObList list;
GetDockingManager()->GetPaneList(list, FALSE, NULL, FALSE);
if (list.GetCount() > 0) {
POSITION pos = list.GetTailPosition();
while (pos != NULL) {
CMFCToolBar* pToolBar = DYNAMIC_DOWNCAST(CMFCToolBar, list.GetPrev(pos));
if (pToolBar != nullptr) {
pToolBar->LoadState(lpszProfileName);
}
}
}
GetDockingManager()->LoadState(lpszProfileName);
GetDockingManager()->SetDockState();
GetDockingManager()->ShowDelayShowMiniFrames(TRUE);
// Trick#4: MFC BUGFIX: force assigning the child frame docking manager to all miniframes (for details look at http://stackoverflow.com/q/39253843/987850).
for (POSITION pos = GetDockingManager()->GetMiniFrames().GetHeadPosition(); pos != NULL;)
{
CWnd* pWndNext = (CWnd*)GetDockingManager()->GetMiniFrames().GetNext(pos);
if (pWndNext != nullptr && pWndNext->IsKindOf(RUNTIME_CLASS(CPaneFrameWnd))) {
STATIC_DOWNCAST(CPaneFrameWnd, pWndNext)->SetDockingManager(GetDockingManager());
}
}
}
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
bool bRes = CMDIChildWndEx::OnCreate(lpCreateStruct) == 0;
if (bRes)
{
// enable docking
EnableDocking(CBRS_ALIGN_ANY);
// enable Visual Studio 2005 style docking window behavior
CDockingManager::SetDockingMode(DT_SMART);
// Creating the pane.
// ID_VIEW_PANE_ID - pane ID, must be declared in resource.h
// CRect(0, 0, 100, 100) - default pane size in floating state (pane is not docked to any frame sides).
// CBRS_LEFT - default side for pane docking.
if (!m_wndPane.Create(_T("My Pane"), this, CRect(0, 0, 100, 100), TRUE, ID_VIEW_PANE_ID, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI)) {
TRACE0("Failed to create Pane\n");
return -1; // failed to create
}
// Enable docking and redocking pane to frame any sides (you can pass a combination of CBRS_ALIGN_ flags)
m_wndPane.EnableDocking(CBRS_ALIGN_ANY);
// Dock pane to the default (left) side of the frame.
DockPane(&m_wndPane);
}
// Loading dock manager state
if (bRes) {
LoadBarState(theApp.GetRegSectionPath(_T("ChildFrame")));
}
return bRes ? 0 : 1;
}
void CChildFrame::OnDestroy()
{
// Save dock manager state
SaveBarState(theApp.GetRegSectionPath(_T("ChildFrame")));
CMDIChildWndEx::OnDestroy();
}