c++ - CPaneFrameWnd 中的 BUG(MFC 功能包 VS 2015)

标签 c++ mfc mfc-feature-pack

微型框架类 CPaneFrameWnd包含智能对接算法中的错误! 此类在 MFC 中用作 float Pane 的迷你框架,并且可以将其停靠到父框架停靠站点或选项卡式 Pane 。当所有 Pane 只能停靠到主框架时,它工作正常,但是当 Pane 停靠到 MDI 应用程序中的子框架时,此类有一个错误。重现错误的步骤:

  1. 取消停靠某些 Pane 以使其处于 float 状态。
  2. 在 MDI 子框架中保存停靠状态:GetDockingManager()->SaveState(...)
  3. 恢复此 MDI 子框架的停靠状态:GetDockingManager()->LoadState(...); GetDockingManager()->SetDockState();
  4. 并尝试通过鼠标将此 Pane 停靠在同一框架侧。
  5. 你不能这样。 Pane 通过鼠标移动到框架一侧但未固定。

CPaneFrameWnd 类源中的错误。在许多地方类使用代码 m_pDockManager != NULL ? m_pDockManager : afxGlobalUtils.GetDockingManager(GetParent()); 用于访问其码头管理器。 但是在类的某些地方,这段代码看起来像 m_pDockManager != NULL ? m_pDockManager:afxGlobalUtils.GetDockingManager(this);。这就是错误的原因 - 全局函数 afxGlobalUtils.GetDockingManager() 无法从 this 指针获取停靠管理器并尝试从 this 指针的父窗口获取它。它看起来像 pManager != NULL ? pManager : GetDockingManager(pWnd->GetParent());。但是 CPaneFrameWnd 类具有 AFxGlobalUtils.GetDockingManager() 无法访问的 INLINE NONVIRTUAL GetParent() 方法。因此,经过一些递归 afxGlobalUtils.GetDockingManager() 返回主应用程序框架的 dockmanager!当然,这个 dockmanager 与 MDI 子框架的 docmanager 不同。

唯一正确的解决方案是更改所有 m_pDockManager != NULL ? m_pDockManager : afxGlobalUtils.GetDockingManager(this);m_pDockManager != NULL ? m_pDockManager:afxGlobalUtils.GetDockingManager(GetParent()); 在 CPaneFrameWnd 源(afxpaneframewnd.cpp 文件)中。 但这需要对 MFC 代码进行修补。我们都知道微软有多懒。 可能有人知道如何修复当前 MFC 版本中的这个错误?

最佳答案

我找到了错误修复的解决方法。如问题所述,主要问题是迷你框架类 CPaneFrameWnd 具有未初始化的 m_pDockManager 属性(它具有 nullptr 值)。因此,在某些情况下,CPaneFrameWnd 类无法从父级找到正确的 dockmanager。该错误的解决方法是强制初始化所有迷你框架 m_pDockManager 属性。这样做的好地方是从注册表恢复对接状态(有问题的第 3 步)。

右保存加载子框架停靠状态示例:

// Save docking state for CChildFrame class (inherited from CMDIChildWndEx)
void CChildFrame::SaveBarState(LPCTSTR lpszProfileName) const
{
    const_cast<CChildFrame*>(this)->GetDockingManager()->SaveState(lpszProfileName);

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

// Restore docking state for CChildFrame class (inherited from CMDIChildWndEx)
void CChildFrame::LoadBarState(LPCTSTR lpszProfileName)
{
    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);

    // MFC BUGFIX: force assigning the child frame docking manager to all miniframes.
    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());
        }
    }
}

如何使用此代码:

// creating child frame and its panes, loading the saved panes docking state.
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    bool bRes = TBase::OnCreate(lpCreateStruct) == 0;
    if (bRes)
    {
        // enable docking
        EnableDocking(CBRS_ALIGN_ANY);

        // enable Visual Studio 2005 style docking window behavior
        CDockingManager::SetDockingMode(DT_SMART);

        // Creating toolbar, statusbar and panes. Dock them to default places.
        {
            // ....
        }
    }

    if (bRes) {
       LoadBarState(theApp.GetRegSectionPath(_T("ChildFrame")));
    }
    return bRes ? 0 : 1;
}


// destroy child frame and save panes docking state.
void CChildFrame::OnDestroy()
{
    SaveBarState(theApp.GetRegSectionPath(_T("ChildFrame")));
    TBase::OnDestroy();
}

Full sample source code .

关于c++ - CPaneFrameWnd 中的 BUG(MFC 功能包 VS 2015),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39253843/

相关文章:

c++ - 我应该在哪里实现我的类方法?

c++ - 如何在 Windows 7 中获取 "temp folder"?

mfc - 检测 MFC 中的模态对话框

visual-c++ - 如何检查弹出菜单项?

c++ - 为什么 SetWindowsHookEx 返回 0?

c++ - 抽象类的链表?

visual-c++ - 将文本框绑定(bind)到 DDX 中的控制与值

c++ - 在 CMFCToolBar 中为顶级 CMFCToolBarButton 设置图像

c++ - 放弃 CMainFrame 中的 ALT 按键操作