One of the most aggravating things about using a tab control is having to use property sheets or embedding child dialogs into the tab control.
Both approaches work, but cause the program to have more code than I think is necessary.
So I decided to create a very simple to use tab control class, which dynamically creates the child controls for you, and also cleans up the memory for you.
This class is very easy to use, and requires very little coding, and should save you a lot of time!
It supports as many tabs as you need right out of the box, and can create as many controls as you need as well!
It allows you to insert a child control into a tab with a single line of code!
It allows for easy access to control values and the ability to modify the controls very easily.
It also supports keyboard navigation though the child controls.
The class was designed for one of my projects "Options" dialog.
The tab class takes care of all object memory cleanup, and dynamically creates and destroys all child window objects for you.
I've tested the class with Win2K and XP, I don't use Win98 anymore, but I'm sure it probably works fine with 98.
It is compatible with XP visual styles, many thanks for the idea I used from another codeproject article for the XP styles compatibility;
( http://www.codeproject.com/wtl/ThemedDialog.asp )
(I create a solid background brush from a pixel of the color we need, rather than using a repositioned
patternbrush of the bitmap)
Using the code
Using the class is simple, just add...
to your dialog's header file like so...
#include "MyTabCtrl.h" //<-- Add here
// COptionsDlg dialog
class COptionsDlg : public CDialog
Next create a public member variable for your
CTabCtrl in your dialog, and add the
WS_TABSTOP style to your
CTabCtrl as well,
once you have done that, change your declared variable from "CTabCtrl" to "CMyTabCtrl" as shown below...
Your now ready to use the tab class! Currently the class has support
You could very easily add support for more controls, just take a look at the code and modify it a little bit.
The following functions are what you use for inserting controls, here is a
explanation of how they work...
uID is the ID of the control you are making, this must be unique of course.
sCaption would be the text in the control.
iY are your X and Y offsets, so you can move your control around within the tab control.
iTab is the tab which you want to insert the control in.
iLen would be the length of the control.
bCheck is the check state of the item once it is created (Checked or Unchecked)
uLocation is a Positioning Helper, it can be one or more of following:
Every time you insert a control, the class remembers the location of the last control inserted,
so you can use
uLocation to position the next control Below (
P_BELOW), and align it with the previous controls left side (
You can use a Bitwise OR to include multiple...
uLocation = P_BELOW|P_LEFT;
P_BELOW = aligns the top of control you are inserting, with the bottom of the previous inserted control
P_TOP = aligns the top of control you are inserting, with the top of the previous inserted control
P_LEFT = aligns the left of control you are inserting, with the left of the previous inserted control
P_RIGHT = aligns the left of control you are inserting, with the right of the previous inserted control
TopOf functions are helper functions for positioning as well.
You can use them to obtain the location of a previously inserted control, then drop that value into your
iY to help position a new control.
void CreateCheckBox(BOOL bCheck, LPCTSTR sCaption, int nID,
int iTab, UINT uLocation = 0, int iX = 0, int iY = 0);
void CreateGroupBox(LPCTSTR sCaption, int nID, int iTab,
int width, int height, UINT uLocation = 0, int iX = 0,
int iY = 0);
void CreateRadioBox(BOOL bCheck, LPCTSTR sCaption, int nID,
int iTab, UINT uLocation = 0, int iX = 0, int iY = 0,
DWORD dwStyle = 0);
void CreateButton(LPCTSTR sCaption, int nID, int iTab,
UINT uLocation = 0, int iX = 0, int iY = 0,
int iLen = 50);
void CreateEdit(LPCTSTR sCaption, int nID, int iTab,
UINT uLocation = 0, int iX = 0, int iY = 0,
int iLen = 100);
void CreateStatic(LPCTSTR sCaption, int nID, int iTab,
UINT uLocation = 0, int iX = 0, int iY = 0);
int BottomOf(int nID);
int RightOf(int nID);
int LeftOf(int nID);
int TopOf(int nID);
CString GetItemText(int nID);
int GetItemTextLength(int nID);
int GetItemTextAsInt(int nID);
BOOL GetItemCheckState(int nID);
Below is an example of the code in use, 9 controls are created in 2 Tabs with only
12 lines of code.
m_cTab.CreateCheckBox(FALSE,"Start Program with
Windows", 2, 0, 0, 10, 15);
m_cTab.CreateCheckBox(FALSE,"Connect to All Contacts
on Startup", 3, 0, P_BELOW | P_LEFT);
m_cTab.CreateGroupBox("New Contact Acceptance", 4, 0,
310, 52, P_BELOW, 0, 10);
m_cTab.CreateRadioBox(FALSE,"Auto Accept All New
Contacts", 5, 0, P_LEFT | P_TOP, 10, 15, WS_GROUP);
m_cTab.CreateRadioBox(TRUE,"New Contacts Must Match
Connection Password", 6, 0, P_BELOW | P_LEFT, 0,
m_cTab.CreateCheckBox(FALSE,"Play Sound When Contact
Comes Online", 9, 1, 0, 10, 15);
m_cTab.CreateCheckBox(FALSE,"Play Sound On New
Messages", 10, 1, P_BELOW | P_LEFT);
As you can see, inserting controls into your tab control looks very nice and neat, and is easy.
But how do we make pushbuttons call functions you ask? The class sends a message to your dialog
WPARAM containing the uID of the button which was pressed, so you can handle any
button event with a single function, and a simple switch, or if/then statement.
LRESULT COptionsDlg::ButtonPressed(WPARAM w, LPARAM l)
TRACE("BUTTON %d was pressed [%s]\n",w,s);
Points of Interest
Buttons and Keyboard Navigation were the tricky parts of the class, examining the code will shed some light on how I addressed the issues,
PreTranslateMessage I trapped some specific messages for keyboard and button events, then I reposted them back to the class as a new type of message for processing,
I did this so that a minimal amount of code would appear in the
PreTranslateMessage message function, to prevent slowing down the message pump.
In order to maneuver through child controls without a mouse, you can use the tab button to move between controls in your dialog (make sure your tab control has
Then once you tab to your
TabControl, use the up and down arrow keys on your keyboard to select the child controls, and space bar to activate or deactivate them.
Jan 2007, Public Release.