CMFCTabCtrl 控件中的bug

作者:吴建凰 Email:wjh_2010@163.com,转载请标明出处,谢谢。

最近在做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;
CStringtitle;

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值