Click here to Skip to main content
13,868,307 members
Click here to Skip to main content


387 bookmarked
Posted 13 Jun 2002

Custom Tab Controls, Tabbed Frame and Tabbed MDI

, 13 Jul 2005
An extensible framework for creating customized tabs in ATL/WTL, with a VS.NET-like tab control implementation, tabbed frames, tabbed MDI, and more.
Sergey Klimov
// History of CoolTabCtrls.h prior to codeproject article about it
// History (Date/Author/Description):
// ----------------------------------
// 2002/06/12: Daniel Bowen
// - Several updates to make CCustomTabCtrl derived classes act
//   more like a common control:
//   * Window Styles: CTCS_*.  Use the same bits as the
//     tab control style where possible
//     (CTCS_BOTTOM == TCS_BOTTOM, etc.)
//   * Notifications: CTCN_*.  Use the same bits as the
//     tab control notifications where possible
//   * Structures:
//   * Better implementation of custom drawing.  It now works much
//     more similar to how custom drawing works for common controls.
//   * Remove the dependancy on REFLECT_NOTIFICATIONS. 
//     No common control needs notifications reflected, and neither
//     does this now.
//   * Don't have the tab itself depend on TCN_INITIALIZE/CTCN_INITIALIZE.
//     There's no precedent for this type of notification in any of the
//     common controls.  Instead, there is an overrideable "Initialize()"
//     function if a derived class needs to do some initialization
//     outside of handling WM_SETTINGCHANGE (which is sent during the
//     default initialization)
//   * Remove "SetSizeSettings" and "GetSizeSettings", since there is
//     no precident for this in a common control, and was mainly being
//     used by derived classes (who have access to the member variable).
// - Style updates:
//   * CTCS_BOLDSELECTEDTAB. New. Instead of exposing methods
//     "SetBoldSelectedTab" and "GetBoldSelectedTab", use this style.
//   * CTCS_TOOLTIPS.  Now required if you want the tab control
//     to use tooltips (instead of always using tooltips).
//   * CTCS_SCROLL. New. This enables "scroll buttons".  When the tab
//     items don't get all the real estate they want, they overflow
//     and the scroll button for that side is enabled.
//     You can call the methods Get/SetScrollDelta and
//     Get/SetScrollRepeat to adjust how much is scrolled, and
//     how fast scrolling is repeated when holding down the button.
//   * CTCS_CLOSEBUTTON. New. This enables a "close" button.  When this
//     button is clicked, the parent gets a "CTCN_CLOSE" notification.
//   * CTCS_HOTTRACK. New. Enables hot tracking tab items.
//     If you are targetting Windows 2000/98 or later, be sure to
//     #define WINVER and/or _WIN32_WINNT to 0x0500 or later before
//     including this file (usually in your precompiled header)
//     so that the new "COLOR_HOTLIGHT" is used.
//     Note:  If you specify CTCS_SCROLL or CTCS_CLOSEBUTTON,
//     those buttons are always hot tracked regardless of this style.

// - Structure updates:
//   * NMCTCCUSTOMDRAW (extends NMCUSTOMDRAW).  The precident for
//     this strucuture are custom draw structures like
//     NMTBCUSTOMDRAW (toolbar) and NMLVCUSTOMDRAW (list view)
//   * TC_SIZES renamed to CTCSETTINGS.  This structure may hold
//     more settings in the future.
// - Tab Item updates:
//   * rename CCoolTabItem to CCustomTabItem
//   * remove HWND from CCustomTabItem, and have new "CTabViewTabItem"
//     that inherits from CCustomTabItem.
// - "State" member variable.  Several state and flag related things
//   are kept as bits in a new state member variable.

// - New default WinTraits used that specifies CTCS_TOOLTIPS by default
// - Message handlers shouldn't be overrideable functions. OnSettingChange
//   was guilty of this.  Change it so that the function isn't called
//   directly, and that each derived class implementing OnSettingChange
//   has the corresponding message map entry for it.
// - EnsureVisible.  Useful when CTCS_SCROLL is set, and you want to see
//   a tab that is scrolled partially or completely out of view.
//   The precident for this is the list view message "LVM_ENSUREVISIBLE".
// - Resizing:
//   * UpdateLayout - In your derived class, instead of overriding UpdateLayout,
//     override UpdateLayout_Default and/or UpdateLayout_ScrollToFit
//   * Override CalcSize_CloseButton and/or CalcSize_ScrollButtons to
//     support the close button and/or scroll buttons
// - Drawing:
//   * Overrideables: DoPrePaint, DoItemPaint, DoPostPaint.  Use the
//     custom draw structure colors, so that you pick up
//     any changes that a client might be doing if they truly are
//     using the custom draw functionality.  Override
//     InitializeDrawStruct to specify the defaults.
//   * Paint the close button and scroll buttons in DoPostPaint
//     (depending if the corresponding styles are set)
// - Move updating of the tab item tool tip RECT updating to
//   "UpdateTabItemTooltipRects" in the base CCustomTabCtrl class.
// - Hit test also can check for close button and scroll buttons.
// - GetItemRect accounts for any scroll offset.
// - Rename m_fontBold to m_fontSel, and always use it as the "selected item font"
// - Handle WM_CAPTURECHANGE and clear any mouse down tracking
//   (even if we do a ReleaseCapture when we have the capture,
//   WM_CAPTURECHANGE is sent).
// - Fix small issues with DeleteAllItems that would cause an ASSERT.
//   The current selection wasn't being properly cleared, and
//   would be erroneously set to 0 (which is a valid index of the first item)
// 2002/05/13: Daniel Bowen (DDB)
// - Compile under VC 7 and update a few minor issues
// 2002/05/09: Daniel Bowen (DDB)
// - Move CDotNetTabCtrl and CDotNetButtonTabCtrl out of CoolTabCtrls.h
//   into DotNetTabCtrl.h. Relevant history also moved.
// - Renamed CFlatTabCtrl to how Bjarke renamed it - CSimpleDotNetTabCtrl
// - Don't depend on atlmisc.h directly (CRect, CPoint, etc.).
//   If compiling under VC7 then you'll need atlstr.h,
//   and if under VC6 then you'll still need atlmisc.h
//   (for the time being...).
// - Update most notifications and operations that could use it
//   (SetCurSel, DeleteItem, etc.) to use the new NMCOOLTABITEM or
// - Use CAtlArray instead of CSimpleArray to hold the items.
//   If compiling under VC 7, use CAtlArray.  If compiling under
//   VC 6, have a "fake" CAtlArray that inherits from CSimpleArray,
//   but has a public interface like CAtlArray.  This was needed
//   so that items can be inserted at any position.
// - Several suggestions from Bjarke Viksoe
//   * DefWindowProc() in OnCreate
//   * bHandled = FALSE in OnDestroy (so others can see the message)
//   * Handle WM_SIZE, and call UpdateLayout.
//   * Call UpdateLayout in OnSettingChange
//   * Updates to CButtonTabCtrl, CFolderTabCtrl, and CSimpleDotNetTabCtrl
// - Let the handler of notifications we send be able to cancel
//   the default handling of LButtonDown, RButtonDown,
//   LButtonDblClk, RButtonDblClk, DeleteItem
//   This is done by returning a non-zero value from the
//   notification handler.
// - Additional arguments to InsertItem to select the item inserted
// - MoveItem and TCN_MOVEITEM
// - SwapItemPositions and TCN_SWAPITEMPOSITIONS
// - Additional arguments to DeleteItem to not get the default
//   handling of changing the selected item if the selected
//   item is being deleted, and an argument to say not to send
//   a notification about the deletion.
// 2002/04/23: Daniel Bowen (DDB)
// - Tooltips. The way we implement tooltips is to have as many
//   "tools" as there are tabs.  The relationship of
//    tool ID => tab index is:
//    tool ID = tab index + 1     (to avoid 0 as an ID)
//   Adding and removing tabs adds and removes the "last" tooltip tool.
//   In UpdateLayout, we set the RECTs for the tooltip tools,
//   and we have it ask us for the text all the time,
//   and give back the text for the corresponding tab.
// - New "CCoolTabItem" class that is used instead of TCITEM.
// - CCustomTabCtrl now has another template parameter - 
//   the items "array" type.  Its default is "CCoolTabItem".
//   If you want, you could use your own class that inherits
//   from CCoolTabItem when creating a new type of tab control.
// - The "sizes" parallel array is now obsoleted, because
//   the RECT for the tab is kept in CCoolTabItem.
// - Several of the public "Interfaces" have been modified. 
//   This was done in conjunction with what would make more
//   sense for CCoolTabItem. Here an attempt at the list:
//    * GetItem - Now returns a pointer to the corresponding tab item
//    * SetItem - I've obsoleted it for now. It can come back
//       if someone really wants it.  Now, instead of calling SetItem,
//       call GetItem to get the pointer to the TItem, update
//       what you want through the methods on TItem (i.e., CCoolTabItem),
//       then call UpdateLayout and Invalidate if appropriate.
//    * FindItem - Since TItem (CCoolTabItem) doesn't have a "mask"
//       guy anymore, FindItem has a new parameter to specify
//       which fields to care about in the search.  These flags
//       are an enumeration in CCoolTabItem.
//    * InsertItem - You can build a TItem instance and pass
//       a pointer to that in, or you can use the new version
//       of InsertItem where you can just pass function arguments.
// - New "CreateNewItem" and "DeleteItem" that manage the memory
//   allocation/deallocation of the TItem;  These are overrideable.
// - Call SetCurSel and SetFocus so that they are overrideable.
// - Some variable renaming.(idx => nIndex, cnt => nCount, etc.)
// - Some format changes in CCustomTabCtrl (tabs, brace placement, etc.)
// 2002/04/22: Daniel Bowen (DDB)
// - Changed "struct TC_SIZES" from using int's to using char's.
//   This makes an instance take up 4 bytes instead of 16 bytes.
// - Move the Image List and "bold selected tab" stuff to the
//   base "CCustomTabCtrl" class.  In a future revision, I'll
//   probably update the base class to honor these, but for
//   now, its up to the deriving class whether or not to use
//   these things.  Having them there makes it easier to use
//   any CCustomTabCtrl derived class where there's a template
//   parameter for the tab class, and where it might want to
//   use the image list or bold the selected tab.
// - When the tabs are not a subclassed static control,
//   the "GetFont" and "SetFont" calls were going in the bit
//   bucket.  CCustomTabCtrl now has a member variable
//   "CFont m_font", and all the derived classes (with one
//   exception) now use the base class version instead of their own.
//   If a derived class has a bold font, they still declare
//   that themselves.  Because the base class "m_font" is
//   managing the lifetime of the font (i.e., it calls DeleteObject
//   in the destructor), the handler for WM_SETFONT makes
//   a copy of the font instead of "pointing" to it.
//   Typically, no one should be sending us WM_SETFONT.
//   CFlatTabCtrl is the one deriving class that has its
//   own version of m_font, because it wants to do a
//   SetFont(m_fontBold) - which with the new way of things
//   would cause CCustomTabCtrl::m_font and CFlatTabCtrl::m_fontBold
//   to be the same.
// 2002/01/30: Daniel Bowen (DDB)
// - Rename Bjarke's original CDotNetTabCtrl to CFlatTabCtrl
// - Added my version of CDotNetTabCtrl based on Pascal's
//   CDotNetTabCtrl2:
//    * Added support for divider lines between tabs
//    * Closer match to look of tabs used in VS.Net
//    * Support for bolding text of selected tab
// - Added CDotNetButtonTabCtrl (to look like VS.Net
//    view of HTML with the Design/HTML buttons)
// - Utilize COffscreenDrawRect for flicker-free drawing
//   (requires updated atlgdix.h containing COffscreenDrawRect)
// - Handle WM_RBUTTONDOWN to allow switching tabs,
//   and fire NM_RCLICK notification
// - When changing the selection, redraw entire client area
//   to avoid visual artifacts in case an implementation
//   doesn't keep each tab the same size always
//   (such as if the selected tab is bolded and slightly bigger,
//   or if there are divider lines between tabs).
// - Added method "FindItem" that let's you search for an item
//   with some or all the members that have the requested values.
//   It is meant to work similar to CListViewCtrl::FindItem and
//   LVM_FINDITEM (since there are no similar messages for tab controls)
// - Updated "DeleteItem" to better handle the case when the
//   deleted item is before the selected item.  It now keeps the
//   same item selected that was selected before, and adjusts
//   m_iCurSel as appropriate (this was needed for doing TabbedMDI.h)
// - Updated "GetItem" so that if bits in the mask are requested
//   that aren't acutally in the stored item, those bits are cleared,
//   so that the caller of "GetItem" can determine which bits
//   it got, compared to which it asked for.
//   This update also allows you to let some tabs have images and
//   others not have images, and have it work properly.
// 11/06/2001: Pascal Binggeli (PBI)
// - Added support for ImageList
// - Added support for not subclassing from CStatic.
// - Added CDotNetTabCtrl2 class.

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Daniel Bowen
United States United States
Daniel Bowen used to work as a Software Engineer for Evans & Sutherland in Salt Lake City, Utah working on the modeling tools for high end flight simulators. He then worked for the startup company WiLife Inc. in Draper, Utah working on the software portion of an easy to use and affordable digital video surveillance system. WiLife Inc. is now part of Logitech Inc.

You may also be interested in...

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190214.1 | Last Updated 14 Jul 2005
Article Copyright 2002 by Daniel Bowen
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid