CMFCTabCtrl 控件中的bug

作者:吴建凰 Email:[email protected],转载请标明出处,谢谢。

最近在做VC的项目,由于VS2008之后增加了一些较为好用的控件,如CMFCRibbonBar、CMFCTabCtrl等,很多人vs2008之后的环境来写应用程序。但是我使用CMFCTabCtrl 时发现了一个比较严重的bug,在网上查找,一直都没有解决。然后我分析控件本身的代码时发现了bug的原因所在。这里我将bug出现的现象和解决办法写出来,希望对大家有用,如果有不对的地方还请大家不吝赐教。

 

我的项目概述:

用vs2010创建一个单文档应用程序(去掉 Document/View architecture support),在在CChildView类中增加CMFCTabCtrl 变量m_wndTabs。在CChildView中响应一个按钮事件增加一个窗口,代码如下:

声明:CMyView :public CView

void CChildView::OnClickAddView()
{
 CMyView*  myView;
 CString  title;

 title.LoadString(IDS_MYVIEW_TITLE);

 if (m_wndTabs.GetTabByLabel(title) >= 0)
 {
  return ;
 }
 myView= new CMyView();
 myView->Create(NULL,title,WS_CHILD | WS_VISIBLE,CRect(0,0,0,0),&m_wndTabs,PREPARE_ATTENDEES);

 m_wndTabs.AddTab(myView,title);

}

创建和显示都好好着,但是当点击m_wndTabs上的关闭按时却会出现异常。网上有些人说将CMyView 的基类改成CWnd,的确在关闭的时候不会出现异常,但只要一刷新就会出现异常。有些人说是因为tab没有移除掉,在关闭的时候移除掉就行。的确如果在关闭的时候执行m_wndTabs.RmoveTab可以正常运行。这样好像把问题解决了,其实不然,而且增加了很多限制,首先窗口的基类必须是CWnd,必须在OnClose或者OnDestroy函数中关闭对应的tab。

如果你的基类必须是CView那就没有办法解决了。我经过分析发现,当点击m_wndTabs上的关闭按钮时会找到对应的窗口句柄,并执行关闭操作,但并没有将当前的tab移除(这就是bug所在)。而RemoveTab也会执行关闭窗口的操作。如果你按照上面的解决办法,就会出现异常。最根本的解决办法应该是重写点击关闭按钮的事件。

经过分析知道,关闭按钮的事件是LButtonUp事件,我们只要重写这个事件即可。

解决办法如下:

定义CMyTabCtrl,基类CMFCTabCtrl。增加ON_WM_LBUTTONUP()事件。

然后重写void CMyTabCtrl::OnLButtonUp(UINT nFlags, CPoint point)函数,代码见后面。这样将基类中的代码重写,就解决了问题。红色部分为我改的代码,其余代码不变。

void CMyTabCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
 if (m_bTrackSplitter || m_bResize)
 {
  StopResize(FALSE);
  m_bTrackSplitter = FALSE;
  m_bResize = FALSE;
  ReleaseCapture();
 }

 if (IsMDITabGroup())
  {
  CPoint pointDelta;
  GetCursorPos(&pointDelta);
  pointDelta = m_ptHot - pointDelta;
  int nDrag = GetSystemMetrics(SM_CXDRAG);
  if (GetCapture() == this && m_bReadyToDetach && (abs(pointDelta.x) > nDrag || abs(pointDelta.y) > nDrag))
  {
   ReleaseCapture();
   if (!IsPtInTabArea(point))
   {
    GetParent()->SendMessage(AFX_WM_ON_MOVETABCOMPLETE, (WPARAM) this, (LPARAM) MAKELPARAM(point.x, point.y));
   }
  }
  else
  {
   ActivateMDITab();
  }
 }
 
 if (m_bTabCloseButtonPressed)
 {
  m_bTabCloseButtonPressed = FALSE;
  m_bTabCloseButtonHighlighted = FALSE;

  RedrawWindow(m_rectCloseButton);

  if (m_rectCloseButton.PtInRect(point))
  {
   CWnd* pWndActive = GetActiveWnd();
   int  index = this->GetActiveTab();
   if (pWndActive != NULL)
   {
    //pWndActive->DestroyWindow();//->SendMessage(WM_CLOSE);
    this->SetActiveTab(index-1);
    this->RemoveTab(index);
   }

   return;
  }
 }

 if (m_iTabBeforeDrag != m_iActiveTab)
 {
  CWnd* pWndParent = GetParent();
  ASSERT_VALID(pWndParent);

  pWndParent->SendMessage(AFX_WM_ON_MOVE_TAB, m_iTabBeforeDrag, m_iActiveTab);
  if (pWndParent->IsKindOf(RUNTIME_CLASS(CBaseTabbedPane)) || pWndParent->IsKindOf(RUNTIME_CLASS(CMDIClientAreaWnd)))
  {
   pWndParent = AFXGetParentFrame(pWndParent);
   if (pWndParent != NULL)
   {
    pWndParent->SendMessage(AFX_WM_ON_MOVE_TAB, m_iTabBeforeDrag, m_iActiveTab);
   }
  }
 }

 if (m_bReadyToDetach)
 {
  m_bReadyToDetach = FALSE;
  ReleaseCapture();

  if (!ActivateOnBtnUp())
  {
   m_iPressed = -1;
   m_iHighlighted = -1;
  }
 }

 if (ActivateOnBtnUp())
 {
  bool bNewActiveTab = m_iActiveTab != m_iHighlighted;

  if (m_iHighlighted == m_iPressed && m_iHighlighted >= 0 && m_iHighlighted != m_iActiveTab)
  {
   m_iLastActiveTab = m_iActiveTab;
   m_bSetActiveTabByMouseClick = TRUE;
   m_bUserSelectedTab = FALSE;

   if (!SetActiveTab(m_iHighlighted))
   {
    m_bSetActiveTabByMouseClick = FALSE;
    m_bUserSelectedTab = FALSE;
    m_iPressed = -1;

    if (!IsOneNoteStyle())
    {
     m_iHighlighted = -1;
    }

    ReleaseCapture();
    return;
   }

   FireChangeActiveTab(m_iActiveTab);
   m_bSetActiveTabByMouseClick = FALSE;
   m_bUserSelectedTab = FALSE;
  }

  int iHighlighted = m_iHighlighted;
  int iPressed = m_iPressed;

  m_iPressed = -1;

  if (!IsOneNoteStyle())
  {
   m_iHighlighted = -1;
  }

  ReleaseCapture();

  if (bNewActiveTab)
  {
   InvalidateTab(iHighlighted);

   if (iPressed != iHighlighted)
   {
    InvalidateTab(iPressed);
   }
  }
 }

 if (IsOneNoteStyle())
 {
  CRect rectTabAreaTop;
  CRect rectTabAreaBottom;

  GetTabArea(rectTabAreaTop, rectTabAreaBottom);

  if (!rectTabAreaTop.IsRectEmpty())
  {
   InvalidateRect(rectTabAreaTop, FALSE);
  }

  if (!rectTabAreaBottom.IsRectEmpty())
  {
   InvalidateRect(rectTabAreaBottom, FALSE);
  }

  UpdateWindow();
 }

 CWnd::OnLButtonUp(nFlags, point);
}

 

你可能感兴趣的:(null,Class,文档,email,2010)