Click here to Skip to main content
Click here to Skip to main content

Tabbed Dialog without Property Sheets

By , 3 Mar 2004
 

Introduction

Have you ever wanted to create a "tabbed window" effect, but found the amount of information on the web lacking? Or, did you find out how to use "property sheets", but thought that method was too complicated?

Background

I was in both of those situations for years, and it seemed like I would never be able to use tabs how I wanted. That I couldn't "get" property sheets may be a testament of my programming ability. Yet, it may have been a more mature programmer who was able to create a different, simpler method. I submit this method in the hope that it's usefulness outweighs any criticisms.

How It Works

Uses normal dialogs, along with some positioning and hiding. Child windows are placed on top of a tab control in the main window.

Getting Started

There are 3 major steps to getting this to work:

  • Prepare the dialogs
  • Set up main dialog's header file
  • Code main dialog's methods

Prepare the Dialogs

Make a tab control on the main window. This is the style I used, but it shouldn't be important:

Create four standard blank dialogs, one for each tab, with these settings (style is important):

Check "Control parent" in "Extended Styles" if you want to be able to tab in and out from the tab control and the child windows. Create the classes for each child dialog as you normally would in ClassWizard, and that's all you need to set up for them! I put a simple, appropriately labeled Static Text control on each one just so I knew that it worked. You can resize the dialogs to the planned run-time size for easier arrangement of their controls, but that will be handled programmatically when the child window is shown.

Set Up Main Dialog's Header File

Include child window header files:

#include "GeneralDlg.h"
#include "DisplayDlg.h"
#include "SoundsDlg.h"
#include "AdvancedDlg.h"

//////////////////////////////////////////////////////////////
// CSettingsDlg dialog

Create instances of the child window classes and the method for switching the child windows:


public:
     CSettingsDlg(CWnd* pParent = NULL); // standard constructor

     CGeneralDlg m_dGeneralDlg;
     CDisplayDlg m_dDisplayDlg;
     CSoundsDlg m_dSoundsDlg;
     CAdvancedDlg m_dAdvancedDlg;

     void ShowWindowNumber(int number);

Create a CRect structure to hold the position of the child windows:


     DECLARE_MESSAGE_MAP()
private:
     CRect m_rSettingsRect;
};

Using ClassWizard, add a WM_SHOWWINDOW message to the main dialog, and a TCN_SELCHANGE message to the tab control.

Code Main Dialog's Methods

Method for showing and hiding child windows:

void CSettingsDlg::ShowWindowNumber(int number)
{
     // This example uses four windows
     int windowCount = 4;

     // Validate the parameter
     if ((number >= 0) && (number < windowCount))
     {
          // Create and assign pointers to each window
          CDialog *m_dPointer[4];

          m_dPointer[0] = &m_dGeneralDlg;
          m_dPointer[1] = &m_dDisplayDlg;
          m_dPointer[2] = &m_dSoundsDlg;
          m_dPointer[3] = &m_dAdvancedDlg;

          // Hide every window except for the chosen one
          for (int count = 0; count < windowCount; count++)
          {
               if (count != number)
               {
                    m_dPointer[count]->ShowWindow(SW_HIDE);
               }
               else if (count == number)
               {
                    // Show the chosen window and set it's location
                    m_dPointer[count]->SetWindowPos(&wndTop, m_rSettingsRect.left,
                         m_rSettingsRect.top, m_rSettingsRect.right,
                         m_rSettingsRect.bottom, SWP_SHOWWINDOW);

                    m_cTab.SetCurSel(count);
               }
          }
     }
}

Whenever the main window is shown, show the first child window:

void CSettingsDlg::OnShowWindow(BOOL bShow, UINT nStatus) 
{
     CDialog::OnShowWindow(bShow, nStatus);

     // When the dialog is shown, display the first window
     if (bShow)
     {
          ShowWindowNumber(0);
     }
}

When a tab is selected, show the right child window:

void CSettingsDlg::OnSelchangeTab(NMHDR* pNMHDR, LRESULT* pResult) 
{
     // Get the number of the currently selected tab, and show its window
     ShowWindowNumber(m_cTab.GetCurFocus());

     // Do something with the "formal parameters" so 
     // the compiler is happy in warning level 4
     pNMHDR = NULL;
     pResult = NULL;
}

Fill out OnInitDialog:

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

     // Set the icon for this dialog.  The framework does this automatically
     //  when the application's main window is not a dialog
     SetIcon(m_hIcon, TRUE);  // Set big icon
     SetIcon(m_hIcon, FALSE); // Set small icon

     CRect tabRect;

     m_cTab.GetWindowRect(tabRect);

     // Set the size and location of the child windows based on the tab control
     m_rSettingsRect.left = 13;
     m_rSettingsRect.top = 44;
     m_rSettingsRect.right = tabRect.Width() - 7;
     m_rSettingsRect.bottom = tabRect.Height() - 38;

     // Create the child windows for the main window class
     m_dGeneralDlg.Create(IDD_GENERAL, this);
     m_dDisplayDlg.Create(IDD_DISPLAY, this);
     m_dSoundsDlg.Create(IDD_SOUNDS, this);
     m_dAdvancedDlg.Create(IDD_ADVANCED, this);

     // This is redundant with the default value, considering what OnShowWindow does
     ShowWindowNumber(0);

     // Set the titles for each tab
     TCITEM tabItem;
     tabItem.mask = TCIF_TEXT;

     tabItem.pszText = _T("  &General   ");
     m_cTab.InsertItem(0, &tabItem);

     tabItem.pszText = _T("  Display   ");
     m_cTab.InsertItem(1, &tabItem);

     tabItem.pszText = _T("  Sounds    ");
     m_cTab.InsertItem(2, &tabItem);

     tabItem.pszText = _T("  Advanced  ");
     m_cTab.InsertItem(3, &tabItem);
     
     return TRUE;  // return TRUE  unless you set the focus to a control
}

Now you can build it and see if it works!

Points of Interest

I didn't realize the current code in ShowWindowNumber right away. It had a lot of if/else if/../else conditions with essentially the same code in each. I would not have shared this code if I had not found a better way to switch the windows. Thus, ShowWindowNumber is the most important and interesting part of this article.

Where Can You Go From Here?

You can use this idea to make a settings dialog in your application.

The members of each tab window can be accessed like this:

// m_dSettingsDlg is the instance of the settings dialog
// m_sString is attached to an EditBox in the General dialog

CString string = m_dSettingsDlg.m_dGeneralDlg.m_sString;

There is a lot of room for improvement if used in an actual application, but this shows the basic implementation.

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

About the Author

Andrew Lawrence
Software Developer
United States United States
Programmer, artist, philosopher.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberTAKS_8925-Mar-13 9:19 
It helped me a lot to work in the dialog tab
GeneralMy vote of 5memberLaxmikant_Yadav19-Apr-12 0:23 
Nice Article
GeneralMy vote of 5memberbreesknees27-Mar-12 23:00 
I think this is the best yet simple solution in tabbed child dialog I've ever found and used.. Thank you so much, Sir... Smile | :)
GeneralMy vote of 5membernetprotector30-Nov-11 20:10 
nice
GeneralMy vote of 5memberYASER RAMDAN12-Jan-11 18:50 
The explanation is very clear and precise, and is submitted in a very simple way.
Thank you Andrew you really helped me.
GeneralLove you!membermaxpaper22-Dec-10 13:55 
Love you!
Generalvery usefull ... THANKS VERY MUCH!!!memberjavikf2t17-Jan-10 9:36 
Merci.
GeneralNeeds urgent helpmemberRoshniRose3-Mar-08 22:22 
How can I add a propertysheet to a propertywindow of a file with particular extension(appears when we rigt click on a particular file and take the properties option)?.For eg,I want to add a new propertysheet to the propertywindow of every file with extension .bmp
Questionpositioning of child windowsmemberMarcia R18-Dec-06 22:24 
hi..
ur code was very useful for my project..
but am facing a problem while executing it..
 
while running the program, the child windows appear as separate window. it is not been displayed in the dialog under the tab control..
 
could anyone who has done this proj successfully can help me out..
thanks...

 
Marcia Smile | :)
AnswerRe: positioning of child windowsmemberScotDolan20-Feb-07 8:12 
Did you ever fix the problem with you child window appearing as a separate window within you tabs application. Cause, I am having the same problem and could use a hand.
 

 
Scott Dolan
Jernie Corporation
Engineering & Manufacturing
Software, Hardware, & Enclosures

AnswerRe: positioning of child windowsmemberSarwar Erfan27-Nov-07 23:22 
Make SURE child window's STYLE is set to CHILD
(And also TitleBar is false)

 
Sarwar Erfan
QuestionHow can we access the variables in one dialog from other dialog.membersheshidar22-Aug-06 23:45 
Hi.
U did a grt job, it is very helpfull for who learn Tab Dialogs.
I have one question.
I created one dialog boxe, and I created a second dialog with tab control. I called the first two dialog boxe in the second one's tab control like how u explained in project.
along with this i had created a database and i created one class for this database.
now what i am trying is creating 5 edit controls on first dialog box and one add button on second dialog box where we have tab control, and update database from the first dialog box.
 
CDialog1
Cdialog2
CdialogDataBase
I have edit boxes in Cdialog1.
Tab control, Add button on Cdialog2 .

So my question is how can I handle the controls on first dialog box from the tab dialog box.
 

OR
 
I can use database form Dialog1.
And I can use database form the dialog1. Then I tried wth calling this dialog1 in tabcontrol in dialog2. So I can Use dialog1 in dialog2. But total application is getting hang when i clicked on the dialog1 embeded in dialog2 tab control.
 
How can I overcome with this problem.
Thank You.
 

 


 
sheshidar
 

-- modified at 6:07 Wednesday 23rd August, 2006
 
Sheshidar
Questionwhat is the limit on the number of tabsmemberamber_jalan2-Apr-06 15:28 
hi,
 
i found this code extremely useful and have used it as a template for designing my application. However i want to know what the maximum limit is on the number of tabs i can have in one main window. VC++ 6 (the version i am using) shows 5 tabs when we create the window initially. Is this the maximum or can i add more tabs?
 
Thanks
Amber

AnswerRe: what is the limit on the number of tabsmemberbinyo665-Jul-09 15:14 
As today according to http://msdn.microsoft.com/en-us/library/ehxs35fh(VS.80).aspx, it is 24 dialogs (strange number Roll eyes | :rolleyes: )
QuestionResizingmemberScaleOvenStove3-Feb-06 9:38 
Any insight on how to get the dialogs on the tab sheets to resize? I have managed to get the tab control to resize when my main dialog is resized, but am having issues getting the dialogs on each tab sheet to resize as well. Has anyone ran into this?
GeneralGREAT EXAMPLEmemberreza6666-Oct-05 3:10 

I was sooo close to give up on the tabs.
I must have tested 10 different project and all of them was crashing (mostly when applied to my own project)or missing something so when i read your article i thought yeah right easy but i gotte say FINALLY i'm able to use this TABS.
 
THANK YOU SIR Cool | :cool:
 
This is The best example for using tabs!
 
/Rico
GeneralAdding Buttons to a Child Windowmembermortonca21-Oct-04 12:48 
Thank you so much for this article! I'm using it to create a series of pages, each of which will have numerous checkboxes from which the user may chose. I would also like to add two buttons: "Check All" and "Clear All" to each page. However, on my first attempt, when I right-clicked on the Check All button, and went to the Class Wizard to add the event handler and code, it took me to the class constructor instead of an "OnCheckAll" function. Is there something special I have to do to add buttons to a child window?
 
Thanks,
Cheryl
GeneralRe: Adding Buttons to a Child Windowmembermortonca22-Oct-04 7:17 
I'm answering myself. This morning my Class Wizard decided to create the OnCheckAll and OnClearAll functions for me just like it's supposed to. Don't know what was going on yesterday...
 
Thanks again for a great article!
 
Cheryl
GeneralA tipmemberFred Koestner11-Mar-04 3:02 
I see a lot of hard-coded values in OnInitDialog:
// Set the size and location of the child windows based on the tab control
m_rSettingsRect.left = 13;
m_rSettingsRect.top = 44;
m_rSettingsRect.right = tabRect.Width() - 7;
m_rSettingsRect.bottom = tabRect.Height() - 38;
 

Here's how I have handled getting the "client" area of a tab control:
CRect rcTabs;
// Set up our tab pages
m_Tabs.GetClientRect( rcTabs );
m_Tabs.ClientToScreen( rcTabs );
ScreenToClient( rcTabs );
m_Tabs.AdjustRect( FALSE, rcTabs );
m_OptionsPage1.Create( m_hWnd, rcTabs );
 
Basically, we are making use of the tab control's built-in support for giving us its client area.
 
Hope this is helpful...
 
BTW, does this work with XP themes? In other words, do the controls like static text and check boxes paint with the almost-white background, or do they still paint with the button-face color?
 
It has been my experience that using tab controls doen't work well in XP. I am currently exploring using a custom CPropertySheet class that turns off all the normal buttons and removes the title and border.
 
-Fred
GeneralRe: A tipmemberAndrew Lawrence11-Mar-04 19:35 
Fred Koestner wrote:
I see a lot of hard-coded values in OnInitDialog:
// Set the size and location of the child windows based on the tab control
m_rSettingsRect.left = 13;
m_rSettingsRect.top = 44;
m_rSettingsRect.right = tabRect.Width() - 7;
m_rSettingsRect.bottom = tabRect.Height() - 38;
 

Here's how I have handled getting the "client" area of a tab control:
CRect rcTabs;
// Set up our tab pages
m_Tabs.GetClientRect( rcTabs );
m_Tabs.ClientToScreen( rcTabs );
ScreenToClient( rcTabs );
m_Tabs.AdjustRect( FALSE, rcTabs );
m_OptionsPage1.Create( m_hWnd, rcTabs );
 
Basically, we are making use of the tab control's built-in support for giving us its client area.
 
Hope this is helpful...
 
BTW, does this work with XP themes? In other words, do the controls like static text and check boxes paint with the almost-white background, or do they still paint with the button-face color?
 
It has been my experience that using tab controls doen't work well in XP. I am currently exploring using a custom CPropertySheet class that turns off all the normal buttons and removes the title and border.

 
That looks good, I'll have to try it. It seems like it would do the same thing with a better method.
 
XP themes? Hmmm | :| Frown | :( Cry | :(( I would have to admit that property sheets and pages would be better for conforming with such a pervasive GUI. Maybe you could use owner-drawn controls along with those classes. I haven't gotten into that yet, but I've seen some cool stuff that other people have done.
 
Thanks for the tip.
 
Andrew
GeneralActually...memberAndrew Lawrence12-Mar-04 2:09 
it was just half complete. Smile | :)
 
Replace
 
m_rSettingsRect.left = 13;
m_rSettingsRect.top = 44;

 
with
 
m_rSettingsRect.left = tabRect.left;
m_rSettingsRect.top = tabRect.top;

 
and the child windows stay inside the tab control if it is moved.
QuestionDoes it handle the tab key to move between child controls properly?memberRancidCrabtree10-Mar-04 4:22 
Considering how many times I've seen solutions like this that DON'T handle tabbing between child controls properly, it would have been nice if you had put controls on your child dialogs.
 
I notice that every time you change tabs, you reposition the the visible child dialog. Not a lot of overhead, I'm sure, but hardly needed. Additionally, the control flow through that function seems backwards:
if(count == number)
{
ShowWindow
}
else
{
HideWindow
}
might be more appropriate.
AnswerRe: Does it handle the tab key to move between child controls properly?memberAndrew Lawrence10-Mar-04 21:51 
RancidCrabtree wrote:
 
Considering how many times I've seen solutions like this that DON'T handle tabbing between child controls properly, it would have been nice if you had put controls on your child dialogs.
 
I notice that every time you change tabs, you reposition the the visible child dialog. Not a lot of overhead, I'm sure, but hardly needed. Additionally, the control flow through that function seems backwards:
if(count == number)
{
ShowWindow
}
else
{
HideWindow
}
might be more appropriate.

 

1. It actually does tab between the child controls properly, I just thought putting controls with tab stops was excessive for a simple, tutorial example. When I update this, I will consider putting some on.
 
Something that might be weird happens when tabbing in and out from the child windows, the tab control, and main window controls: when preparing this example (I based it on code from an application I made first), I forgot to set the tab control as the LAST tab number. Otherwise, it should work as expected.
 
2. The efficiency in showing and positioning the windows is certainly arguable; you have produced some good criticisms. I'll explain why I chose to do it that way.
 
a. I figured that each child window needing to be positioned properly before it is shown the first time is a given.
 
If a certain child window is shown more than once, than the method is less efficient, assuming that the main window wasn't moved (in that case the child window would have to be moved to keep the same position in the main window, and keeping track of the change would increase the overhead).
 
If not every child window needs to be shown, then it is more efficient to position the child windows "on demand" (they are not repositioned when hidden).
 
b. I thought that the most common condition should be the first condition tested, the second common the second tested, etc. Practically speaking, there are a lot more windows that are going to be hidden than shown. Even if there are only two child windows, the probability that a window needs to be shown isn't greater than 50%.
 
I hope that I've answered your questions. This was the first critical response-to-a-response that I've written on CP for one of my articles.
 
Thank you for being the first to respond to it. Smile | :)
 
Andrew
GeneralData Exchange Between Dialogmemberwellem24-Aug-04 2:55 
Hi, Andrew
Thank's for your code, it's useful for me
Actually I'm a novice in MFC Programming,
so can you give more detail explanation how to
exchange data between dialog ?, for instance:
I have an Edit Box IDC_EDIT1
(with member variable m_Edit1) in CDisplayDlg
and an Edit Box IDC_EDIT2
(with member variable m_Edit2) in CGeneralDlg
and a Button IDC_BUTTON1 in CGeneralDlg

How to make a code in (BN_CLICKED) the map function
OnButton1() to send (copy) the value from m_Edit1
to m_Edit2 ?.
 
Comment from anybode else are open
 
Thank's
Wellem D.Wenur
GeneralRe: Data Exchange Between DialogmemberAndrew Lawrence24-Aug-04 11:46 
It can be hard to make two classes that are controlled by another class (I like to think of them as siblings) have the ability to communicate with each other, so I always try to find another way to do what I need to do, but it is possible. One way would be to let the class that has control over both classes act as an intermediary.
 
First, you need to get a pointer to the controlling class (CSettingsDlg). Then, add a function in CDisplayDlg to return m_Edit1 (I assume it's a CString). Next, add a function in CSettingsDlg which will take the returned CString and return it, which you will call in the BN_CLICKED method. Then you can assign the return from the function in CSettingsDlg to m_Edit2.
 

 
void BN_CLICKED method in CGeneralDlg:
 
// Get a pointer to the parent class
CSettingsDlg *pWnd = (CSettingsDlg*)GetParent();
 
m_Edit2 = pWnd->GetEdit1TextFromDisplay();
 
UpdateData(false);
 

 
CString GetEdit1TextFromDisplay() function in CSettingsDlg:
 
return m_dDisplayDlg.GetText();
 

 
CString GetText() function in CDisplayDlg:
 
return m_Edit1;

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 4 Mar 2004
Article Copyright 2004 by Andrew Lawrence
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid