Click here to Skip to main content
15,881,882 members
Articles / Desktop Programming / MFC
Article

CMyTabCtrl - Very Simple Dynamic Tab Control for MFC

Rate me:
Please Sign up or sign in to vote.
4.58/5 (28 votes)
31 Jan 20074 min read 190.7K   14.2K   86   27
A Very Simple to use and expandable CTabCtrl Class
Sample image

Introduction

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.

Background

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...

#include "CMyTabClass.h"

to your dialog's header file like so...

#pragma once
#include "afxcmn.h"

#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...

public:
     //CTabCtrl m_cTab; //<-- comment this out
     CMyTabCtrl m_cTab; //<-- add this

Your now ready to use the tab class! Currently the class has support for: GroupBoxes, RadioBoxes, Checkboxes, Buttons, and Statictext 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.
  • iX and 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: P_TOP, P_LEFT, P_RIGHT, P_BELOW 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 (P_LEFT) 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

The BottomOf, RightOf, LeftOf, and 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 iX or iY to help position a new control.

//control creation functions
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);

//placment helpers
int BottomOf(int nID);
int RightOf(int nID);
int LeftOf(int nID);
int TopOf(int nID);

//get value helpers
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.

BOOL COptionsDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    //Must call this first to initialize the tab class
    m_cTab.Init();

    m_cTab.InsertItem(0,"Connections");
    m_cTab.InsertItem(1,"Notifications");

    //CONNECTIONS
    m_cTab.CreateGroupBox("Startup",1,0,310,52);
    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, 
          0, 0);
    
    //NOTIFICATIONS
    m_cTab.CreateGroupBox("Sounds",8,1,310,52);
    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);    
    
    //Just a simple test to demonstrate how easy it is to 
    //access the controls
    CString s;
    
    //get the text from control number 8
    m_cTab.GetDlgItem(8)->GetWindowText(s);
    TRACE("TEST: %s\n",s);    
    
    return TRUE;
}

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 with the 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.

BEGIN_MESSAGE_MAP(COptionsDlg, CDialog)
    ON_MESSAGE(WM_BUTTONPRESSED,ButtonPressed)
END_MESSAGE_MAP()


LRESULT COptionsDlg::ButtonPressed(WPARAM w, LPARAM l)
{
    CString s;
    m_cTab.GetDlgItem((int)w)->GetWindowText(s);
    TRACE("BUTTON %d was pressed [%s]\n",w,s);
    
    return 0;
}

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, Using 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 WS_TABSTOP style) 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.

History

Jan 2007, Public Release.

License

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


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAdding custom control Pin
Member 853403527-Oct-16 8:20
Member 853403527-Oct-16 8:20 
QuestionMFC Tab Control Development Video Pin
Jack123sweet5-Apr-14 8:09
Jack123sweet5-Apr-14 8:09 
GeneralMy vote of 3 Pin
Member 101906786-Nov-13 20:31
Member 101906786-Nov-13 20:31 
QuestionWant to add CSliderCtrl on Tab Pin
nothingmag18-Jul-12 19:31
nothingmag18-Jul-12 19:31 
GeneralMy vote of 5 Pin
Member 87736342-Apr-12 6:22
Member 87736342-Apr-12 6:22 
GeneralMy vote of 4 Pin
ghinton19-Feb-12 10:51
ghinton19-Feb-12 10:51 
QuestionItem colors Pin
ghinton19-Feb-12 10:50
ghinton19-Feb-12 10:50 
General[My vote of 2] function '__thiscall CMyTabCtrl::CMyTabCtrl(void)' already has a body Pin
xiang_yan8-Sep-10 0:02
xiang_yan8-Sep-10 0:02 
QuestionCan it use in a SDI formview based app? Pin
maplewang26-May-09 4:07
maplewang26-May-09 4:07 
QuestionHow to change the font name, size, caption for edit control Pin
santhosh33kumar15-Sep-08 3:44
santhosh33kumar15-Sep-08 3:44 
Generalerror in VS2005 Pin
PoweRoy12-Aug-08 2:48
PoweRoy12-Aug-08 2:48 
Im trying to compile the source code but I keep getting a
"mytabctrl.cpp(21) : error C2600: 'CMyTabCtrl::CMyTabCtrl' : cannot define a compiler-generated special member function (must be declared in the class first)"
GeneralRe: error in VS2005 Pin
PoweRoy12-Aug-08 3:07
PoweRoy12-Aug-08 3:07 
GeneralRe: error in VS2005 Pin
Axter26-Apr-09 1:52
professionalAxter26-Apr-09 1:52 
QuestionAccelerators (&) Pin
Ray Bolger14-May-08 5:17
Ray Bolger14-May-08 5:17 
QuestionCan made scrollable property page? Pin
Shock20-Jan-08 9:05
Shock20-Jan-08 9:05 
GeneralDoesn't work on Visual Studio 6.0 Pin
victoria195019-Jan-08 3:27
victoria195019-Jan-08 3:27 
QuestionHow to add Dialogs derived from CDialog to CPropertySheet Pin
hari prasad sathpadi5-Sep-07 1:04
hari prasad sathpadi5-Sep-07 1:04 
GeneralCMyTabCtl and Visual Studio 6.0 Pin
morgan.hallgren22-Aug-07 23:30
morgan.hallgren22-Aug-07 23:30 
GeneralLicence Pin
df4944-Mar-07 3:38
df4944-Mar-07 3:38 
QuestionCreateGroupBox(...) Pin
Daeha6-Feb-07 21:32
Daeha6-Feb-07 21:32 
GeneralCMyTabCtl and Visual Studio 2005 Pin
neuhaj0e5-Feb-07 11:58
neuhaj0e5-Feb-07 11:58 
AnswerRe: CMyTabCtl and Visual Studio 2005 Pin
neuhaj0e6-Feb-07 12:53
neuhaj0e6-Feb-07 12:53 
AnswerRe: CMyTabCtl and Visual Studio 2005 Pin
indrekm10-Feb-07 1:40
indrekm10-Feb-07 1:40 
GeneralRe: CMyTabCtl and Visual Studio 2005 Pin
PoweRoy12-Aug-08 3:25
PoweRoy12-Aug-08 3:25 
GeneralRe: CMyTabCtl and Visual Studio 2005 Pin
RHBrooks15-Jun-09 9:59
professionalRHBrooks15-Jun-09 9:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.