Tabbed Views

This program is useful if you need multiple views of the same document but you only want one of them to be visible at once. It draws a number of tabs under the menu/toolbars that can be used to select the active view.

First we create a CWnd-derived class

class CTabWnd : public CWnd
 // Construction
 // Generated message map functions
 afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 afx_msg void OnPaint();
Then we add a Create function to initialize our control.

BOOL CTabWnd::Create(DWORD dwStyle, CWnd* pParentWnd, UINT nID)
 CRect rect, parent;

 if (!CWnd::Create(NULL, "SealiteTabWnd", WS_CHILD|WS_VISIBLE, rect, pParentWnd, nID, NULL))
  return FALSE;

 return TRUE;
As you can see the window is created with an uninitialized rect. It will be resized as soon as it receives a WM_SIZEPARENT function. It will fill the entire client area and the views are creating using it as a parent. Because the control is created in the client area of the parent window it has to draw itself the client edge. For best results it is recommended that you remove the WS_EX_CLIENTEDGE style of the parent window.

Next, we add a linked list of views and a CreateView function to add views in it.

typedef struct
 CWnd *pWnd;
 char szLabel[32];
 int x_min, x_max;

BOOL CreateView(LPCTSTR lpszLabel, CRuntimeClass *pViewClass, CCreateContext *pContext);
In the CreateView function we instantiate the pViewClass using CRuntimeClass::CreateObject() and add the resulting CView to the list.

The created views have to be resized whenever the parent window resize, so we add a handler for WM_SIZEPARENT.

LRESULT CTabWnd::OnSizeParent(WPARAM, LPARAM lParam)
 return 0;
To find out how much space is available for the client area after the control bars have allocated space for themselves we have to use the RepositionBars function with the CWnd::reposQuery parameter. Unfortunatly in a WM_SIZEPARENT handler RepositionBars does not return a valid rectangle because the message is used by the control bars to resize the client area. To walk around this we post a message and in its handler we can safely call this function.

The next thing to do is handle the WM_PAINT message to draw the selector and the WM_LBUTTONUP message to switch the views. The SetActiveView function is used to make sure that the window/command messages are routed to the active view. When the current tab is changed both the view thats going to be selected and the one thats going to be unselected are sent the WM_TABCHANGED message.

This is a rather complex subject and it is imposible to throughly cover it in a few pages. For more information you should go through the CTabWnd (you can download the project and source files by clicking the link at the beginning of this page) and CSplitterWnd.

Download demo project - 25 KB

Date Last Updated: April 3, 1999


  • add/del & scroll function?

    Posted by ahgu on 05/10/2006 11:22pm

    Can you add delete/new control to add/delete tabs? Also cursor to move if too many?

  • Shortcut

    Posted by Legacy on 11/16/2002 12:00am

    Originally posted by: Jag

    How add shortcut key for the tab?
    And how to set tab order?


  • Cannot get client area size in OnInitUpdate()

    Posted by Legacy on 07/29/2002 12:00am

    Originally posted by: YI Ren

    I have a SDI application, after adding the tabs, in the view's OnInitUpdate(), GetClientRect() returns 0 size of the view. I really need to know the size of the cient area, so I can resize it to 640x480. Any ideas why the ckient area has a size of zero ?

  • Yes it works on my SDI application

    Posted by Legacy on 05/29/2002 12:00am

    Originally posted by: Guillermo Simanavicius

    Yes, it works on my SDI application, please take a look on my sample at

  • No one above could solved the print preview problem

    Posted by Legacy on 05/22/2002 12:00am

    Originally posted by: Kivanc Oner & Ali Yazgan

    The techniques that both Marc Allaire and Guillermo Simanavicius are not capable of solving the print preview problem. Or we could not use the new codes efficiently. If anyone knows how to print preview from a CListView based SDI application please contact us...

  • Sheetal's exclusive page

    Posted by Legacy on 04/10/2002 12:00am

    Originally posted by: Sheetal

    This is a very nice site but of no use.............

  • Print Preview bug fix

    Posted by Legacy on 03/22/2002 12:00am

    Originally posted by: Marc Allaire

    I investigated a little in the print preview bug.

    It gives an assertion in the file winfrm.cpp, line 1842

    Looking at that i see it tries to get the handle of
    the control/window that has AFX_IDW_PANE_FIRST as its ID.
    This should be the main pane, that print preview hides
    before displaying the preview.

    All there is to do to fix the problem is create the
    CTabWnd window with AFX_IDW_PANE_FIRST for its ID
    and print preview works.

  • Print Preview Crashes

    Posted by Legacy on 04/22/1999 12:00am

    Originally posted by: Jose Luis Flores

    When I try to make a print preview the program crashes but when I try to print I have no problem, so ???? I have spent time looking for the error but ... without luck !

  • Problem with CRichEditView

    Posted by Legacy on 02/19/1999 12:00am

    Originally posted by: Emil Dimitrov

    When sitching from one tab to another CTabWnd sends notification messages WM_TABCHANGED to both child views.
    This is a problem when using CRichEditView as a view because the message WM_TABCHANGED has the same value as EM_SETTARGETDEVICE. After sending this message to the CRichEditView it stops responding.

    To avoid this problem do not use WM_USER but WM_APP because MFC uses user messages for some controls :

    #define WM_TABRESIZE (WM_APP+0x47)
    #define WM_TABCHANGED (WM_APP+0x48)

  • Using it with splitter window

    Posted by Legacy on 11/06/1998 12:00am

    Originally posted by: Rochmad Setyadi

    I modified the code ...CTabWnd class no longer derived from CWnd ...
    but CSplitterWnd satisfy my GUI ... and simply it's work! There
    are some modifications on OnTabRize ..handler.

    Here my modification ...

    CWnd *pParent=(CWnd *)GetParent();
    CWnd *coClient = ((CSplitterWnd*)pParent)->GetPane(0,0);

    CRect coRect;
    CRect parRect;

    int pw = parRect.Width();
    int ph = parRect.Height();
    int wco = coRect.Width();

    CRect rect(wco,0,pw,ph);

    m_bLock= TRUE;
    CRect rct(0,0,32000,32000);
    pParent->RepositionBars(0, 0xffff,AFX_IDW_PANE_FIRST,
    CWnd::reposQuery,&rct, NULL);


    CWnd *pWnd;
    for (POSITION pos=m_viewList.GetHeadPosition(); pos;
    pWnd->MoveWindow(2, TABWND_HEIGHT+2, rect.Width()-10,

    return 0;

    CTabWnd object was created using CreateStatic ..from CSplitterWnd ..instead of CTabWnd's Create

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Moving from an on-premises environment to Office 365 does not remove the need to plan for disruptions or reduce the business risk requirements for protecting email services. If anything, some risks increase with a move to the cloud. Read how to ease the transition every business faces if considering or already migrating to cloud email. This white paper discusses: Setting expectations when migrating to Office 365 Understanding the implications of relying solely on Exchange Online security Necessary archiving …

  • U.S. companies are desperately trying to recruit and hire skilled software engineers and developers, but there's simply not enough quality talent to go around. In response, companies often resort to inferior solutions -- hiring substandard developers and engineers, recruiting talent on a part-time or temporary basis, poaching people from competitors, or burdening an already stressed IT staff for more of their labor. Fortunately, there's a better solution. Read this white paper to learn the business value of …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date