用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,有不合理的地方或有更好的方法歡迎提出。