2016-08-31 10 views
1

miniframeクラスCPaneFrameWndには、スマートドッキングアルゴリズムのバグが含まれています。 このクラスは、フローティングペインのミニフレームとしてMFCで使用され、親フレームドックサイトまたはタブ付きペインにドッキングすることができます。すべてのペインをメインフレームのみにドッキングできればうまく動作しますが、MDIアプリケーションでペインが子フレームにドッキングされると、このクラスにはバグがあります。バグを再現する手順:CPaneFrameWndのBUG(MFC Feature Pack VS 2015)

  1. 一部のペインをフロート状態にすると、ドッキング解除されます。 MDI子フレーム内
  2. 保存ドッキング状態:GetDockingManager()->SaveState(...)
  3. このMDI子フレームのためのドッキング状態を復元します。GetDockingManager()->LoadState(...); GetDockingManager()->SetDockState();
  4. とマウスで同じフレーム側に、このペインをドッキングしてみてください。
  5. これはできません。ペインはマウスでフレーム側に移動されますが、固定されていません。

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()はMAINアプリケーションフレームのdockmanagerを返します!もちろん、このドックマネージャーはMDI子フレームのdocmanagerと同じではありません。

正しい解決策は、CPaneFrameWndソース(afxpaneframewnd.cppファイル)のm_pDockManager != NULL ? m_pDockManager : afxGlobalUtils.GetDockingManager(this);からm_pDockManager != NULL ? m_pDockManager : afxGlobalUtils.GetDockingManager(GetParent());にすべて変更することです。 しかし、これにはMFCコードへのパッチが必要です。 Microsoftの怠け者は、私たちの誰もが知っています。 誰かが現在のMFCリリースでこのバグを修正する方法を知っていることがありますか?

+0

子フレームへのドッキングがサポートされているという印象を与えたのは何ですか? –

+0

MS – 23W

+0

によって文書化されるようになる*「現在のMFCリリースでこのバグを修正する方法を誰かが知っていることがありますか?」* - これは、 "Right right solution is ... "*。バグが見つかった場合は、そのバグを修正したい場合は、製品のベンダー([Microsoft Connect](https://connect.microsoft.com/VisualStudio))に問い合わせる必要があります。 – IInspectable

答えて

1

バグ修正の回避策が見つかりました。問題のように、ミニフレームクラスCPaneFrameWndが初期化されていない(nullptr値)m_pDockManagerプロパティを持っているという主な問題。したがって、クラスCPaneFrameWndは、親から正しいドックマネージャーを見つけることができない場合があります。バグの回避策は、すべてのミニフレームを強制的に初期化することです。m_pDockManagerこのための良い場所は、レジストリからの復元ドッキング状態です(問題のステップ3)。

右セーブとロード子フレームのドッキング状態のサンプル:

// 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

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

がどのように-にこのコードを使用します。