Click here to Skip to main content
11,582,850 members (71,304 online)
Click here to Skip to main content

Tabbed Dialog without Property Sheets

, 3 Mar 2004 110.1K 5.5K 68
Rate this:
Please Sign up or sign in to vote.
How to use CDialog pointers to make a tab-controlled window

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

Share

About the Author

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

You may also be interested in...

Comments and Discussions

 
QuestionGood Stuff - my vote of 5 (little big problem though) Pin
Ron Anders28-May-15 11:38
memberRon Anders28-May-15 11:38 
AnswerRe: Good Stuff - my vote of 5 (little big problem though) Pin
Ron Anders5-Jun-15 2:31
memberRon Anders5-Jun-15 2:31 
Questionprogrammatically select a different tab Pin
victoria195020-Oct-13 20:21
membervictoria195020-Oct-13 20:21 
GeneralMy vote of 5 Pin
TAKS_8925-Mar-13 9:19
memberTAKS_8925-Mar-13 9:19 
GeneralMy vote of 5 Pin
Laxmikant_Yadav19-Apr-12 0:23
memberLaxmikant_Yadav19-Apr-12 0:23 
GeneralMy vote of 5 Pin
breesknees27-Mar-12 23:00
memberbreesknees27-Mar-12 23:00 
GeneralMy vote of 5 Pin
netprotector30-Nov-11 20:10
membernetprotector30-Nov-11 20:10 
GeneralMy vote of 5 Pin
YASER RAMDAN12-Jan-11 18:50
memberYASER RAMDAN12-Jan-11 18:50 
GeneralLove you! Pin
maxpaper22-Dec-10 13:55
membermaxpaper22-Dec-10 13:55 
Generalvery usefull ... THANKS VERY MUCH!!! Pin
javikf2t17-Jan-10 9:36
memberjavikf2t17-Jan-10 9:36 
GeneralNeeds urgent help Pin
RoshniRose3-Mar-08 22:22
memberRoshniRose3-Mar-08 22:22 
Questionpositioning of child windows Pin
Marcia R18-Dec-06 22:24
memberMarcia R18-Dec-06 22:24 
AnswerRe: positioning of child windows Pin
ScotDolan20-Feb-07 8:12
memberScotDolan20-Feb-07 8:12 
AnswerRe: positioning of child windows Pin
Sarwar Erfan27-Nov-07 23:22
memberSarwar Erfan27-Nov-07 23:22 
QuestionHow can we access the variables in one dialog from other dialog. Pin
sheshidar22-Aug-06 23:45
membersheshidar22-Aug-06 23:45 
Questionwhat is the limit on the number of tabs Pin
amber_jalan2-Apr-06 15:28
memberamber_jalan2-Apr-06 15:28 
AnswerRe: what is the limit on the number of tabs Pin
binyo665-Jul-09 15:14
memberbinyo665-Jul-09 15:14 
QuestionResizing Pin
ScaleOvenStove3-Feb-06 9:38
memberScaleOvenStove3-Feb-06 9:38 
GeneralGREAT EXAMPLE Pin
reza6666-Oct-05 3:10
memberreza6666-Oct-05 3:10 
GeneralAdding Buttons to a Child Window Pin
mortonca21-Oct-04 12:48
membermortonca21-Oct-04 12:48 
GeneralRe: Adding Buttons to a Child Window Pin
mortonca22-Oct-04 7:17
membermortonca22-Oct-04 7:17 
GeneralA tip Pin
Fred Koestner11-Mar-04 3:02
memberFred Koestner11-Mar-04 3:02 
GeneralRe: A tip Pin
Andrew Lawrence11-Mar-04 19:35
memberAndrew Lawrence11-Mar-04 19:35 
GeneralActually... Pin
Andrew Lawrence12-Mar-04 2:09
memberAndrew Lawrence12-Mar-04 2:09 
QuestionDoes it handle the tab key to move between child controls properly? Pin
RancidCrabtree10-Mar-04 4:22
memberRancidCrabtree10-Mar-04 4:22 
AnswerRe: Does it handle the tab key to move between child controls properly? Pin
Andrew Lawrence10-Mar-04 21:51
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

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

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

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