Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
One sample of CTabbedViewView in D:\Program Files\AllVCLanguageSamples\C++\MFC\Visual C++ 2008 Feature Pack\TabbedView. It is a simple sample to illustate how to use CTabView.

One method of CTabView is RemoveView(). Here I want to Remove the selected view dynamically. The modified sample code is as follows
void CTabbedViewView::OnContextMenu(CWnd*, CPoint point)
{
	//theApp.ShowPopupMenu (IDR_CONTEXT_MENU, point, this);
	CMenu PopupMenu;
	PopupMenu.CreatePopupMenu();
	PopupMenu.AppendMenu(MF_STRING|MF_ENABLED,1,L"Close");
	int nCmdID = PopupMenu.TrackPopupMenu(TPM_LEFTALIGN|TPM_RETURNCMD|TPM_LEFTBUTTON,point.x,point.y,this);
	CMFCTabCtrl& wndTab = GetTabControl();
	CPoint ptTab = point;
	wndTab.ScreenToClient(&ptTab);
	const int nTab = wndTab.GetTabFromPoint(ptTab);
	
	if (nCmdID == 1)
	{
		if (nTab >= 0)
		{
			wndTab.SetActiveTab(nTab);
			RemoveView(nTab);
		}	
	}
}

Here modify the code of OnCreate to enable auto destroy.
C#
int CTabbedViewView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CTabView::OnCreate(lpCreateStruct) == -1)
        return -1;

    CMFCTabCtrl& wndTab = GetTabControl();
    wndTab.AutoDestroyWindow(TRUE);

    AddView (RUNTIME_CLASS (CView1), _T("Simple"), 100);
    AddView (RUNTIME_CLASS (CView2), _T("List"), 101);
    AddView (RUNTIME_CLASS (CView3), _T("Form"), 102);
    AddView (RUNTIME_CLASS (CView4), _T("ScrollView"), 102);

    return 0;
}


But I found one bug when close one of the view. One assertion issue.

What's wrong? How to fix? Appreciated for yr input.
Posted
Comments
Dalek Dave 7-Sep-10 11:38am    
Reason for my vote of 5
Well laid out question.

Please set a break point at void CWnd::AssertValid() const
before the removing and step it thru to see the reason.

Please post the found reason here :)
 
Share this answer
 
v2
Ohmmms...

Here are some findings. When enable m_bAutoDestroyWindow, the code will be run ....
C#
BOOL CMFCBaseTabCtrl::RemoveTab(int iTab, BOOL bRecalcLayout)
{
    if (iTab < 0 || iTab >= m_iTabsNum)
    {
        TRACE(_T("RemoveTab: illegal tab number %d\n"), iTab);
        return FALSE;
    }

    if (m_iTabsNum == 1)
    {
        RemoveAllTabs();
        return TRUE;
    }

    CMFCTabInfo* pTab = (CMFCTabInfo*) m_arTabs [iTab];
    ASSERT_VALID(pTab);

    if (m_pToolTip->GetSafeHwnd() != NULL)
    {
        m_pToolTip->DelTool(this, pTab->m_iTabID);
    }

    //----------------------------
    // Detach tab from collection:
    //----------------------------
    m_arTabs.RemoveAt(iTab);
    m_iTabsNum --;

    //-----------------------------------
    // Destroy tab window and delete tab:
    //-----------------------------------
    if (m_bAutoDestroyWindow)
    {
        ASSERT_VALID(pTab->m_pWnd);
        pTab->m_pWnd->DestroyWindow();
    }

    delete pTab;

    int iActiveTab = m_iActiveTab;
    if (m_iActiveTab >= iTab)
    {
        if (m_bActivateLastVisibleTab)
        {
            GetLastVisibleTab(iActiveTab);
        }
        else
        {
            // Find the next best tab to be activated
            for (int i = m_iTabsNum - 1; i >= 0; --i)
            {
                CMFCTabInfo* pNextActiveTab = (CMFCTabInfo*) m_arTabs [i];
                ASSERT_VALID(pNextActiveTab);

                if (i < iTab && iActiveTab >= 0 && iActiveTab < m_iTabsNum)
                {
                    break;
                }

                if (pNextActiveTab->m_bVisible)
                {
                    iActiveTab = i;
                }
            }
        }

        m_iActiveTab = -1;
    }

    OnChangeTabs();

    if (bRecalcLayout)
    {
        RecalcLayout();
        if (iActiveTab != -1)
        {
            if (m_bActivateLastActiveTab &&(m_iLastActiveTab != -1))
            {
                int iLastActiveTab = m_iLastActiveTab;
                if (iTab < m_iLastActiveTab)
                {
                    iLastActiveTab = m_iLastActiveTab -1;
                }

                int iTabToActivate = -1;
                GetFirstVisibleTab(iLastActiveTab, iTabToActivate);

                SetActiveTab(iTabToActivate);
            }
            else
            {
                int iTabToActivate = -1;
                GetFirstVisibleTab(iActiveTab, iTabToActivate);
                SetActiveTab(iTabToActivate);
            }

            FireChangeActiveTab(m_iActiveTab);
        }
    }

    return TRUE;
}


Here the code block should be noticed.
C#
//-----------------------------------
// Destroy tab window and delete tab:
//-----------------------------------
if (m_bAutoDestroyWindow)
{
    ASSERT_VALID(pTab-&gt;m_pWnd);
    pTab-&gt;m_pWnd-&gt;DestroyWindow();
}

It destroy the view window in the tab control. If the view is the last view which tab number is the biggest in the tab. It will cause problem.

The assertion is caused by ....
void CMFCTabCtrl::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CMemDC memDC(dc, this);
	CDC* pDC = &memDC.GetDC();
	dc.GetClipBox(&m_rectCurrClip);
	COLORREF clrDark;
	COLORREF clrBlack;
	COLORREF clrHighlight;
	COLORREF clrFace;
	COLORREF clrDarkShadow;
	COLORREF clrLight;
	CBrush* pbrFace = NULL;
	CBrush* pbrBlack = NULL;
	CMFCVisualManager::GetInstance()->GetTabFrameColors(this, clrDark, clrBlack, clrHighlight, clrFace, clrDarkShadow, clrLight, pbrFace, pbrBlack);
	ASSERT_VALID(pbrFace);
	ASSERT_VALID(pbrBlack);
	CRect rectClient;
	GetClientRect(&rectClient);
	CBrush* pOldBrush = pDC->SelectObject(pbrFace);
	ENSURE(pOldBrush != NULL);
	CPen penDark(PS_SOLID, 1, clrDark);
	CPen penBlack(PS_SOLID, 1, clrBlack);
	CPen penHiLight(PS_SOLID, 1, clrHighlight);
	CPen* pOldPen = (CPen*) pDC->SelectObject(&penDark);
	ENSURE(pOldPen != NULL);
	const int nTabBorderSize = GetTabBorderSize();
	CRect rectTabs = rectClient;
	if (m_location == LOCATION_BOTTOM)
	{
		rectTabs.top = m_rectTabsArea.top;
	}
	else
	{
		rectTabs.bottom = m_rectTabsArea.bottom;
	}
	pDC->ExcludeClipRect(m_rectWndArea);
	BOOL bBackgroundIsReady = CMFCVisualManager::GetInstance()->OnEraseTabsFrame(pDC, rectClient, this);
	if (!m_bDrawFrame && !bBackgroundIsReady)
	{
		pDC->FillRect(rectClient, pbrFace);
	}
	CMFCVisualManager::GetInstance()->OnEraseTabsArea(pDC, rectTabs, this);
	CRect rectFrame = rectClient;
	if (nTabBorderSize == 0)
	{
		if (m_location == LOCATION_BOTTOM)
		{
			rectFrame.bottom = m_rectTabsArea.top + 1;
		}
		else
		{
			rectFrame.top = m_rectTabsArea.bottom - 1;
		}
		if (m_bFlat)
		{
			pDC->FrameRect(&rectFrame, pbrBlack);
		}
		else
		{
			pDC->FrameRect(&rectFrame, pbrFace);
		}
	}
	else
	{
		int yLine = m_location == LOCATION_BOTTOM ? m_rectTabsArea.top : m_rectTabsArea.bottom;
		if (!m_bFlat)
		{
			if (m_location == LOCATION_BOTTOM)
			{
				rectFrame.bottom = m_rectTabsArea.top;
			}
			else
			{
				rectFrame.top = m_rectTabsArea.bottom;
			}
		}
		//-----------------------------------------------------
		// Draw wide 3-dimensional frame around the Tabs area:
		//-----------------------------------------------------
		if (m_bFlatFrame)
		{
			CRect rectBorder(rectFrame);
			if (m_bFlat)
			{
				if (m_location == LOCATION_BOTTOM)
				{
					rectBorder.bottom = m_rectTabsArea.top + 1;
				}
				else
				{
					rectBorder.top = m_rectTabsArea.bottom - 1;
				}
			}
			rectFrame.DeflateRect(1, 1);
			if (m_bDrawFrame && !bBackgroundIsReady && rectFrame.Width() > 0 && rectFrame.Height() > 0)
			{
				pDC->PatBlt(rectFrame.left, rectFrame.top, nTabBorderSize, rectFrame.Height(), PATCOPY);
				pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), nTabBorderSize, PATCOPY);
				pDC->PatBlt(rectFrame.right - nTabBorderSize - 1, rectFrame.top, nTabBorderSize + 1, rectFrame.Height(), PATCOPY);
				pDC->PatBlt(rectFrame.left, rectFrame.bottom - nTabBorderSize, rectFrame.Width(), nTabBorderSize, PATCOPY);
				if (m_location == LOCATION_BOTTOM)
				{
					pDC->PatBlt(rectFrame.left, m_rectWndArea.bottom, rectFrame.Width(), rectFrame.bottom - m_rectWndArea.bottom, PATCOPY);
				}
				else
				{
					pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), m_rectWndArea.top - rectFrame.top, PATCOPY);
				}
			}
			if (m_bFlat)
			{
				//---------------------------
				// Draw line below the tabs:
				//---------------------------
				pDC->SelectObject(&penBlack);
				pDC->MoveTo(rectFrame.left + nTabBorderSize, yLine);
				pDC->LineTo(rectFrame.right - nTabBorderSize, yLine);
			}
			pDC->Draw3dRect(&rectBorder, clrFace, clrFace);
			if (GetTabsHeight() == 0)
			{
				pDC->Draw3dRect(&rectBorder, clrFace, clrFace);
			}
			else
			{
				if (m_bDrawFrame)
				{
					pDC->Draw3dRect(&rectBorder, clrDark, clrDark);
				}
				if (!m_bIsOneNoteStyle)
				{
					int xRight = rectBorder.right - 1;
					if (!m_bDrawFrame)
					{
						xRight -= nTabBorderSize;
					}
					if (m_location == LOCATION_BOTTOM)
					{
						pDC->SelectObject(&penBlack);
						pDC->MoveTo(rectBorder.left, rectBorder.bottom - 1);
						pDC->LineTo(xRight, rectBorder.bottom - 1);
					}
					else
					{
						pDC->SelectObject(&penHiLight);
						pDC->MoveTo(rectBorder.left, rectBorder.top);
						pDC->LineTo(xRight, rectBorder.top);
					}
				}
			}
		}
		else
		{
			if (m_bDrawFrame)
			{
				pDC->Draw3dRect(&rectFrame, clrHighlight, clrDarkShadow);
				rectFrame.DeflateRect(1, 1);
				pDC->Draw3dRect(&rectFrame, clrLight, clrDark);
				rectFrame.DeflateRect(1, 1);
				if (!bBackgroundIsReady && rectFrame.Width() > 0 && rectFrame.Height() > 0)
				{
					pDC->PatBlt(rectFrame.left, rectFrame.top, nTabBorderSize, rectFrame.Height(), PATCOPY);
					pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), nTabBorderSize, PATCOPY);
					pDC->PatBlt(rectFrame.right - nTabBorderSize, rectFrame.top, nTabBorderSize, rectFrame.Height(), PATCOPY);
					pDC->PatBlt(rectFrame.left, rectFrame.bottom - nTabBorderSize, rectFrame.Width(), nTabBorderSize, PATCOPY);
					if (m_location == LOCATION_BOTTOM)
					{
						pDC->PatBlt(rectFrame.left, m_rectWndArea.bottom, rectFrame.Width(), rectFrame.bottom - m_rectWndArea.bottom, PATCOPY);
					}
					else
					{
						pDC->PatBlt(rectFrame.left, rectFrame.top, rectFrame.Width(), m_rectWndArea.top - rectFrame.top, PATCOPY);
					}
					if (m_bFlat)
					{
						//---------------------------
						// Draw line below the tabs:
						//---------------------------
						pDC->SelectObject(&penBlack);
						pDC->MoveTo(rectFrame.left + nTabBorderSize, yLine);
						pDC->LineTo(rectFrame.right - nTabBorderSize, yLine);
					}
					if (nTabBorderSize > 2)
					{
						rectFrame.DeflateRect(nTabBorderSize - 2, nTabBorderSize - 2);
					}
					if (rectFrame.Width() > 0 && rectFrame.Height() > 0)
					{
						pDC->Draw3dRect(&rectFrame, clrDarkShadow, clrHighlight);
					}
				}
				else
				{
					rectFrame.DeflateRect(2, 2);
				}
			}
		}
	}
	if (m_bTopEdge && m_location == LOCATION_TOP)
	{
		pDC->SelectObject(&penDark);
		pDC->MoveTo(rectClient.left, m_rectTabsArea.bottom);
		pDC->LineTo(rectClient.left, rectClient.top);
		pDC->LineTo(rectClient.right - 1, rectClient.top);
		pDC->LineTo(rectClient.right - 1, m_rectTabsArea.bottom);
	}
	CFont* pOldFont = pDC->SelectObject(m_bFlat ? &m_fntTabs : &afxGlobalData.fontRegular);
	ENSURE(pOldFont != NULL);
	pDC->SetBkMode(TRANSPARENT);
	pDC->SetTextColor(afxGlobalData.clrBtnText);
	if (m_rectTabsArea.Width() > 5 && m_rectTabsArea.Height() > 5)
	{
		//-----------
		// Draw tabs:
		//-----------
		CRect rectClip = m_rectTabsArea;
		rectClip.InflateRect(1, nTabBorderSize);
		CRgn rgn;
		rgn.CreateRectRgnIndirect(rectClip);
		for (int i = m_iTabsNum - 1; i >= 0; i--)
		{
			CMFCTabInfo* pTab = (CMFCTabInfo*) m_arTabs [i];
			ASSERT_VALID(pTab);
			if (!pTab->m_bVisible)
				continue;
			m_iCurTab = i;
			if (i != m_iActiveTab) // Draw active tab last
			{
				pDC->SelectClipRgn(&rgn);
				if (m_bFlat)
				{
					pDC->SelectObject(&penBlack);
					DrawFlatTab(pDC, pTab, FALSE);
				}
				else
				{
					Draw3DTab(pDC, pTab, FALSE);
				}
			}
		}
		if (m_iActiveTab >= 0)
		{
			//-----------------
			// Draw active tab:
			//-----------------
			pDC->SetTextColor(afxGlobalData.clrWindowText);
			CMFCTabInfo* pTabActive = (CMFCTabInfo*) m_arTabs [m_iActiveTab];
			ASSERT_VALID(pTabActive);
			m_iCurTab = m_iActiveTab;
			pDC->SelectClipRgn(&rgn);
			if (m_bFlat)
			{
				pDC->SelectObject(&m_brActiveTab);
				pDC->SelectObject(&m_fntTabsBold);
				pDC->SetTextColor(GetActiveTabTextColor());
				pDC->SelectObject(&penBlack);
				DrawFlatTab(pDC, pTabActive, TRUE);
				//---------------------------------
				// Draw line bellow the active tab:
				//---------------------------------
				const int xLeft = max( m_rectTabsArea.left + 1, pTabActive->m_rect.left + 1);
				if (pTabActive->m_rect.right > m_rectTabsArea.left + 1)
				{
					CPen penLight(PS_SOLID, 1, GetActiveTabColor());
					pDC->SelectObject(&penLight);
					if (m_location == LOCATION_BOTTOM)
					{
						pDC->MoveTo(xLeft, pTabActive->m_rect.top);
						pDC->LineTo(pTabActive->m_rect.right, pTabActive->m_rect.top);
					}
					else
					{
						pDC->MoveTo(xLeft, pTabActive->m_rect.bottom);
						pDC->LineTo(pTabActive->m_rect.right, pTabActive->m_rect.bottom);
					}
					pDC->SelectObject(pOldPen);
				}
			}
			else
			{
				if (m_bIsActiveTabBold)
				{
					if (!IsMDITabGroup() || m_bIsActiveInMDITabGroup)
					{
						pDC->SelectObject(&afxGlobalData.fontBold);
					}
				}
				Draw3DTab(pDC, pTabActive, TRUE);
			}
		}
		pDC->SelectClipRgn(NULL);
	}
	if (!m_rectTabSplitter.IsRectEmpty())
	{
		pDC->FillRect(m_rectTabSplitter, pbrFace);
		CRect rectTabSplitter = m_rectTabSplitter;
		pDC->Draw3dRect(rectTabSplitter, clrDarkShadow, clrDark);
		rectTabSplitter.DeflateRect(1, 1);
		pDC->Draw3dRect(rectTabSplitter, clrHighlight, clrDark);
	}
	if (m_bFlat && m_nTabsHorzOffset > 0)
	{
		pDC->SelectObject(&penDark);
		const int xDivider = m_rectTabsArea.left - 1;
		if (m_location == LOCATION_BOTTOM)
		{
			pDC->MoveTo(xDivider, m_rectTabsArea.top + 1);
			pDC->LineTo(xDivider, m_rectTabsArea.bottom - 2);
		}
		else
		{
			pDC->MoveTo(xDivider, m_rectTabsArea.bottom);
			pDC->LineTo(xDivider, m_rectTabsArea.top + 2);
		}
	}
	if (!m_rectResize.IsRectEmpty())
	{
		pDC->FillRect(m_rectResize, pbrFace);
		pDC->SelectObject(&penDark);
		if (m_ResizeMode == RESIZE_VERT)
		{
			pDC->MoveTo(m_rectResize.left, m_rectResize.top);
			pDC->LineTo(m_rectResize.left, m_rectResize.bottom);
		}
		else
		{
			pDC->MoveTo(m_rectResize.left, m_rectResize.top);
			pDC->LineTo(m_rectResize.right, m_rectResize.top);
		}
	}
	pDC->SelectObject(pOldFont);
	pDC->SelectObject(pOldBrush);
	pDC->SelectObject(pOldPen);
	if (memDC.IsMemDC())
	{
		dc.ExcludeClipRect(m_rectWndArea);
	}
}

Here please take care of the following codes
C#
if (m_iActiveTab >= 0)
{
    //-----------------
    // Draw active tab:
    //-----------------
    pDC->SetTextColor(afxGlobalData.clrWindowText);

    CMFCTabInfo* pTabActive = (CMFCTabInfo*) m_arTabs [m_iActiveTab];
    ASSERT_VALID(pTabActive);

CMFCTabInfo* pTabActive = (CMFCTabInfo*) m_arTabs [m_iActiveTab];
will cause the bug. m_iActiveTab tells the acitive tab. But now it was deleted. so it will cause one out of range of the array assertion.

Now my workarroud is to destroy the view windows out of here. But destroy it in the sample application after RemoveView() like this.
MIDL
RemoveView(nTab);
                //pView->DestroyWindow();


But I think it may be not a good way to deal with this bug. Any comments?
 
Share this answer
 
Comments
Eugen Podsypalnikov 8-Sep-10 2:21am    
It could be tested as well :) :
{
..
m_wndTab.LockWindowUpdate(TRUE);
// Removing
// ..
m_wndTab.LockWindowUpdate(FALSE);
..
}
SAMZCN 9-Sep-10 4:08am    
Thanks. I will try it. It could improve the update performance. ;)
Whenever you hit an assertion it's the programmer of the thing that's throwing the assertion way of telling you you've messed something up. You've broken a class invariant or you're doing some daft. When it fires debug the program and find out what caused the assertion and then you'll either get an idea of what to do to put it right or have something a bit more concrete to ask us.

Cheers,

Ash
 
Share this answer
 
I think index is incorect:
AddView (RUNTIME_CLASS (CView4), _T("ScrollView"), 103); //was 102
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900