用VC2008风格做界面,CMFCTabCtrl是选项卡控制类,其中有EnableActiveTabCloseButton(BOOL)方法可以显示关闭按钮"X",但是点击以后无反应,再次点击程序就会崩溃,这是MFC的一个bug,详见此讨论帖:http://topic.csdn.net/u/20110120/12/35f605e1-f94f-4ff9-a470-d36124816b2b.html
此帖中comey楼主解决了问题,他是这样做的:
1,新建一个类继承自CMFCTabCtrl,即class CMyMFCTabCtrl : public CMFCTabCtrl
2,重写OnCommand函数:
BOOL CMyMFCTabCtrl::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: 在此添加专用代码和/或调用基类
if ((HWND)lParam == m_btnClose.GetSafeHwnd())
{
int nTab = GetActiveTab();
if (nTab >= 0)
{
RemoveTab(nTab);
}
return true;
}
return CMFCTabCtrl::OnCommand(wParam, lParam);
}
(这段如果comey兄觉得不合适请提醒)
但是我运行后,发现有问题:
1,点击“X”后无法响应次函数。
2,通过点击选项本身进入此函数,发现m_btnClose为空,查了一下代码,发现:
只用在m_bScroll为真的情况下才会创建m_btnClose,代码如下:
int CMFCTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMFCBaseTabCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
CRect rectDummy(0, 0, 0, 0);
if (m_bScroll)
{
//-----------------------
// Create scroll buttons:
//-----------------------
if (m_bFlat)
{
m_btnScrollFirst.Create(_T(""), WS_CHILD | WS_VISIBLE, rectDummy, this, (UINT) -1);
m_btnScrollFirst.SetStdImage(CMenuImages::IdArrowFirst);
m_btnScrollFirst.m_bDrawFocus = FALSE;
m_btnScrollFirst.m_nFlatStyle = CMFCButton::BUTTONSTYLE_FLAT;
m_lstButtons.AddTail(m_btnScrollFirst.GetSafeHwnd());
}
m_btnScrollLeft.Create(_T(""), WS_CHILD | WS_VISIBLE, rectDummy, this, (UINT) -1);
m_btnScrollLeft.SetStdImage(m_bFlat ? CMenuImages::IdArrowLeftLarge : CMenuImages::IdArrowLeftTab3d,
m_bIsOneNoteStyle || m_bIsVS2005Style || m_bFlat ? CMenuImages::ImageBlack : CMenuImages::ImageDkGray,
m_bFlat ?(CMenuImages::IMAGES_IDS) 0 : CMenuImages::IdArrowLeftDsbldTab3d);
m_btnScrollLeft.m_bDrawFocus = FALSE;
m_btnScrollLeft.m_nFlatStyle = CMFCButton::BUTTONSTYLE_FLAT;
if (!m_bIsOneNoteStyle && !m_bIsVS2005Style)
{
m_btnScrollLeft.SetAutorepeatMode(50);
}
m_lstButtons.AddTail(m_btnScrollLeft.GetSafeHwnd());
m_btnScrollRight.Create(_T(""), WS_CHILD | WS_VISIBLE, rectDummy, this, (UINT) -1);
m_btnScrollRight.SetStdImage( m_bFlat ? CMenuImages::IdArrowRightLarge : CMenuImages::IdArrowRightTab3d,
m_bIsOneNoteStyle || m_bIsVS2005Style || m_bFlat ? CMenuImages::ImageBlack : CMenuImages::ImageDkGray,
m_bFlat ?(CMenuImages::IMAGES_IDS) 0 : CMenuImages::IdArrowRightDsbldTab3d);
m_btnScrollRight.m_bDrawFocus = FALSE;
m_btnScrollRight.m_nFlatStyle = CMFCButton::BUTTONSTYLE_FLAT;
if (!m_bIsOneNoteStyle && !m_bIsVS2005Style)
{
m_btnScrollRight.SetAutorepeatMode(50);
}
m_lstButtons.AddTail(m_btnScrollRight.GetSafeHwnd());
if (m_bFlat)
{
m_btnScrollLast.Create(_T(""), WS_CHILD | WS_VISIBLE, rectDummy, this, (UINT) -1);
m_btnScrollLast.SetStdImage(CMenuImages::IdArrowLast);
m_btnScrollLast.m_bDrawFocus = FALSE;
m_btnScrollLast.m_nFlatStyle = CMFCButton::BUTTONSTYLE_FLAT;
m_lstButtons.AddTail(m_btnScrollLast.GetSafeHwnd());
}
m_btnClose.Create(_T(""), WS_CHILD | WS_VISIBLE, rectDummy, this, (UINT) -1);
m_btnClose.SetStdImage(CMenuImages::IdClose, m_bIsOneNoteStyle || m_bIsVS2005Style || m_bFlat ? CMenuImages::ImageBlack : CMenuImages::ImageDkGray);
m_btnClose.m_bDrawFocus = FALSE;
m_btnClose.m_nFlatStyle = CMFCButton::BUTTONSTYLE_FLAT;
m_lstButtons.AddTail(m_btnClose.GetSafeHwnd());
if (!m_bFlat && m_bScroll)
{
CString str;
ENSURE(str.LoadString(IDS_AFXBARRES_CLOSEBAR));
m_btnClose.SetTooltip(str);
ENSURE(str.LoadString(IDP_AFXBARRES_SCROLL_LEFT));
m_btnScrollLeft.SetTooltip(str);
ENSURE(str.LoadString(IDP_AFXBARRES_SCROLL_RIGHT));
m_btnScrollRight.SetTooltip(str);
}
}
if (m_bSharedScroll)
{
m_wndScrollWnd.Create(WS_CHILD | WS_VISIBLE | SBS_HORZ, rectDummy, this, (UINT) -1);
}
if (m_bFlat)
{
//---------------------
// Create active brush:
//---------------------
m_brActiveTab.CreateSolidBrush(GetActiveTabColor());
}
else
{
//---------------------------------------
// Text may be truncated. Create tooltip.
//---------------------------------------
if (CTooltipManager::CreateToolTip(m_pToolTip, this, AFX_TOOLTIP_TYPE_TAB))
{
m_pToolTip->SetWindowPos(&wndTop, -1, -1, -1, -1, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSIZE);
}
}
CTooltipManager::CreateToolTip(m_pToolTipClose, this, AFX_TOOLTIP_TYPE_TAB);
if (afxGlobalData.m_hcurStretch == NULL)
{
afxGlobalData.m_hcurStretch = AfxGetApp()->LoadCursor(AFX_IDC_HSPLITBAR);
}
if (afxGlobalData.m_hcurStretchVert == NULL)
{
afxGlobalData.m_hcurStretchVert = AfxGetApp()->LoadCursor(AFX_IDC_VSPLITBAR);
}
SetTabsHeight();
return 0;
}
所以我使用楼主comey的方法失败。
通过跟踪OnLButtonDown方法,在基类中,会做这样的处理
void CMFCBaseTabCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_bWindowPosChanged = FALSE;
CWnd::OnLButtonDown(nFlags, point);
if (m_rectCloseButton.PtInRect(point)) //会进入这里
{
m_bTabCloseButtonPressed = TRUE; //改变了这个标志位
RedrawWindow(m_rectCloseButton);
return; //然后return掉
}
返回上一层,在CMFCTabCtrl中:
void CMFCTabCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_rectTabSplitter.PtInRect(point))
{
m_bTrackSplitter = TRUE;
SetCapture();
return;
}
if (m_ResizeMode != RESIZE_NO && m_rectResize.PtInRect(point))
{
RECT rectBounds;
LRESULT lResult = GetParent()->SendMessage(AFX_WM_GETDRAGBOUNDS, (WPARAM)(LPVOID)this, (LPARAM)(LPVOID) &rectBounds);
m_rectResizeBounds = rectBounds;
if (lResult != 0 && !m_rectResizeBounds.IsRectEmpty())
{
m_bResize = TRUE;
SetCapture();
m_rectResizeDrag = m_rectResize;
ClientToScreen(m_rectResizeDrag);
CRect rectEmpty;
rectEmpty.SetRectEmpty();
DrawResizeDragRect(m_rectResizeDrag, rectEmpty);
return;
}
}
if (IsMDITabGroup())
{
int nTab = GetTabFromPoint(point);
if (nTab == m_iActiveTab)
{
ActivateMDITab(nTab);
}
}
CMFCBaseTabCtrl::OnLButtonDown(nFlags, point); //这里返回
if (!m_bReadyToDetach) //进入这里
{
CWnd* pWndTarget = FindTargetWnd(point);
if (pWndTarget != NULL) //pWndTarget为空,导致消息发不出去
{
ASSERT_VALID(pWndTarget);
MapWindowPoints(pWndTarget, &point, 1);
pWndTarget->SendMessage(WM_LBUTTONDOWN, nFlags, MAKELPARAM(point.x, point.y));
}
}
}
于是我只能在CMyTabCtrl中响应OnLButtonDown消息,把基类的消息覆盖,代码如下:
void CMyTabCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CMFCTabCtrl::OnLButtonDown(nFlags, point); //先执行基类操作,否则会丢失窗口焦点
if (m_rectCloseButton.PtInRect(point))
{
int nTab = GetActiveTab();
if (nTab >= 0)
{
CWnd* pWnd = GetTabWnd (nTab);
pWnd->DestroyWindow ();
delete pWnd;
RemoveTab(nTab); //先删除窗口,再删除tab
}
}
}
在我这里实现了想要的效果,即可以关闭tab,有不合理的地方或有更好的方法欢迎提出。