解决CMFCTabCtrl点“X”无法关闭Tab的方法

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

你可能感兴趣的:(null,delete,Class,mfc,scroll)