Introduction
This article has been updated and the details are available in the Updates section.
It's amazing what a bit of illness can do for your productivity at home! I've been ill for the last couple of days and here it is, yet another extended control. This time my target is the tab control. It seems to me that the tab control is a bit out of fashion these days with people preferring property sheets and pages instead. Well, I tried to use them and couldn't get the result I wanted. I needed some controls above the tabbed area (a bit like the demo, but a few more controls) and I just couldn't get that with property sheets and pages.
The problem with the standard tab control is that involves loads of effort to get pages in to the tab control. A quick in the MSDN located an example by Jeff Prosise that implemented a tabbed view, CTabView, allowing the tabs to be specified as dialog resources. Well, when it comes to code I'm a bit like I was as a ten year old with watches: I've got to fiddle about with it and see how it works and wonder how it could be better.
The first problem was that Jeff's solution was for a view class and I needed a control. The transition here is fairly easy as the view just wraps up the control. The second issue I had was that it didn't give much flexibility over what you could do with the tab pages. "How do I handle events within the page?" I asked myself. The answer was to allow the client to inherit from the tab page class and allow tab page objects to be added to the control and not just dialog resources.
Why is that solutions always present a new problem? By allowing this approach I then introduced a tricky situation with releasing the tab pages. Internally the tab control was creating the tab pages on the heap and maintaining a list of the pages that had been added. When the control was destroyed delete
was called on each page in turn. This obviously causes problems if the pages are created outside of the tab control and passed in to it. In this case the tab control would destroy the pages created externally and then the parent dialog would try and do the same!!
After a lengthy discussion in the Visual C++ forum with Tomasz Sowinski we came up with the answer. The tab control has a protected function for adding pages to the control and the public functions call this one. What is actually passed in to the protected function is a structure containing a pointer to a tab page and a flag indicating if the tab control should destroy it. This way if the tab page is passed in then the flag is set to false, but if the tab control creates the tab page the flag is set to true. At destruction time only the pages with the flag set are destroyed by the tab control. All others are someone else's problem.
How to use it
- Create a dialog resource and class for the dialog that you want a tab control on.
- Add a tab control from the toolbox to the dialog resource.
- Create a member variable for the tab control and change it's type to
CTabCtrlSSL
.
- In the
OnInitDialog
handler for your dialog class create and add any pages you want on the tab control.
BOOL CCTabCtrlSSL_demoDlg::OnInitDialog () {
CDialog::OnInitDialog ();
int nPageID = 0;
m_tabDemo.AddSSLPage (_T("Basic Tab"), nPageID++, IDD_TAB_BASIC);
m_advancedTab.Create (IDD_TAB_ADVANCED, this);
m_tabDemo.AddSSLPage (_T("Advanced Page"), nPageID++, &m_advancedTab);
m_aboutTab.Create (IDD_TAB_ABOUT, this);
m_tabDemo.AddSSLPage (_T("About"), nPageID++, &m_aboutTab);
return TRUE;
}
As you can see from the above code sample (taken from the demo), to add a basic tab call AddSSLPage
and just pass it the resource ID of the dialog resource. For an advanced tab, create the dialog resource setting the window style to Child and the border style to none, then call AddSSLPage
and pass in a pointer to your CTabPageSSL
-derived object.
To continue the style I started with CButtonSSL, here is the documentation for the classes:
Overview | Class members | Known problems
CTabCtrlSSL
A "tab control" is analogous to the dividers in a notebook or the labels in a file cabinet. By using a tab control, an application can define multiple pages for the same area of a window or dialog box. Each page consists of a set of information or a group of controls that the application displays when the user selects the corresponding tab. CTabCtrlSSL
extends the standard tab control by providing an interface for adding tabs to the control that are defined as dialog resources. These tabs are defined internally as CTabPageSSL objects. There is also an interface for adding objects of this type to the tab control allowing custom objects to be defined by inheriting from CTabPageSSL
.
Any tab pages that are created by CTabCtrlSSL
(those where the tab is added by passing in a dialog resource id) are destroyed by CTabCtrlSSL
. It is the clients responsibility to destroy any CTabPageSSL
-derived objects that are passed in to CTabCtrlSSL
.
#include "TabCtrlSSL.h"
Overview | Class members | Known problems
Construction
Page functions
Overridables
OnInitPage |
Override in derived class to initialize pages. |
OnActivatePage |
Override in derived class to respond to page activations. |
OnDeactivatePage |
Override in derived class to respond to page deactivations. |
OnDestroyPage |
Override in derived class to free resources. |
Overview | Class members | Known problems
CTabCtrlSSL::CTabCtrlSSL
CTabCtrlSSL ();
Remarks
Constructs a CTabCtrlSSL
object.
Overview | Class members | Known problems
CTabCtrlSSL::AddSSLPage
int AddSSLPage (LPCTSTR pszTitle, int nPageID, int nTemplateID);
int AddSSLPage (LPCTSTR pszTitle, int nPageID, LPCTSTR pszTemplateName);
int AddSSLPage (LPCTSTR pszTitle, int nPageID, CTabPageSSL* pTabPage);
Return Value
Zero-based index of the new tab if successful; otherwise �1.
Parameters
pszTitle
Address of a null-terminated string that contains the tab text.
nPageID
The zero-based numeric ID for referring to the page to be added.
nTemplateID
Specifies the tab page�s resource ID.
pszTemplateName
Points to a null-terminated string that contains the name of the dialog resource to load.
pTabPage
Points to the CTabPageSSL
object to be added.
Remarks
Call this function to insert a new tab in an existing tab control.
Overview | Class members | Known problems
See Also CTabCtrlSSL::RemoveSSLPage, CTabCtrlSSL::GetSSLPage
CTabCtrlSSL::RemoveSSLPage
BOOL RemoveSSLPage (int nIndex);
Return Value
Nonzero if successful; otherwise 0.
Parameters
nIndex
Zero-based value of the item to delete.
Remarks
Call this function to remove the specified item from a tab control.
Overview | Class members | Known problems
See Also CTabCtrlSSL::AddSSLPage, CTabCtrlSSL::GetSSLPage
CTabCtrlSSL::GetSSLPageCount
int GetSSLPageCount ();
Return Value
Number of items in the tab control.
Remarks
Call this function to retrieve the number of tabs in the tab control.
Overview | Class members | Known problems
CTabCtrlSSL::GetSSLPageTitle
BOOL GetSSLPageTitle (int nIndex, CString& strTitle);
Return Value
Nonzero if successful; otherwise 0.
Parameters
nIndex
Zero-based value of the relevant tab page.
strTitle
A CString
reference to receive the title of the tab page.
Remarks
Call this function to retrieve the title of the specified item in a tab control.
Overview | Class members | Known problems
See Also CTabCtrlSSL::SetSSLPageTitle
CTabCtrlSSL::SetSSLPageTitle
BOOL SetSSLPageTitle (int nIndex, LPCTSTR pszTitle);
Return Value
Nonzero if successful; otherwise 0.
Parameters
nIndex
Zero-based value of the relevant tab page.
pszTitle
Points to a null-terminated string that contains the title to set for the specified tab page.
Remarks
Call this function to set the title of the specified item in a tab control.
Overview | Class members | Known problems
See Also CTabCtrlSSL::GetSSLPageTitle
CTabCtrlSSL::GetSSLPageID
int GetSSLPageID (int nIndex);
Return Value
Zero-based page identifier if successful; otherwise -1.
Parameters
nIndex
Zero-based index of the relevant tab page.
Remarks
Call this function to retrieve the page identifier as specified when the tab was added of the specified item in a tab control. The page identifier may also be altered with the SetSSLPageID function.
Overview | Class members | Known problems
See Also CTabCtrlSSL::SetSSLPageID
CTabCtrlSSL::SetSSLPageID
int SetSSLPageID (int nIndex, int nPageID);
Return Value
The previous zero-based page identifier if successful; otherwise -1.
Parameters
nIndex
Zero-based index of the relevant tab page.
nPageID
The new zero-based page identifier to be set.
Remarks
Call this function to alter the page identifier for a specified tab page.
Overview | Class members | Known problems
See Also CTabCtrlSSL::GetSSLPageID
CTabCtrlSSL::ActivateSSLPage
BOOL ActivateSSLPage (int nIndex);
Return Value
Nonzero if successful; otherwise 0.
Parameters
nIndex
Zero-based index of the tab page to activate.
Remarks
Call this function to change the active tab.
Overview | Class members | Known problems
See Also CTabCtrlSSL::GetSSLActivePage
CTabCtrlSSL::GetSSLActivePage
int GetSSLACtivePage ();
Return Value
Zero-based index of the selected tab if successful or �1 if no tab is selected.
Remarks
Call this function to retrieve the currently selected tab in a tab control.
See Also CTabCtrlSSL::ActivateSSLPage
Overview | Class members | Known problems
CTabCtrlSSL::GetSSLPage
CWnd* GetSSLPage (int nIndex);
Return Value
A pointer to the requested tab page if successful; otherwise NULL.
Parameters
nIndex
Zero-based index of the tab page to get.
Remarks
Call this function to get a specific tab in the tab control.
Overview | Class members | Known problems
CTabCtrlSSL::GetSSLPageIndex
int GetSSLPageIndex (int nPageID);
Return Value
Zero-based index of the specified tab if successful; otherwise -1.
Parameters
nPageID
Zero-based page identifier of the relevant tab page.
Remarks
Call this function to retrieve the zero-based index of a tab page in the tab control.
Overview | Class members | Known problems
CTabCtrlSSL::OnInitPage
BOOL OnInitPage (int nIndex, int nPageID);
Return Value
Nonzero if successful; otherwise 0.
Parameters
nIndex
Zero-based index of the tab page being initialized.
nPageID
Zero-based page identifier of the page being initialized.
Remarks
Override in derived class to initialize pages.
Overview | Class members | Known problems
CTabCtrlSSL::OnActivatePage
void OnActivatePage (int nIndex, int nPageID);
Parameters
nIndex
Zero-based index of the tab page being activated.
nPageID
Zero-based page identifier of the page being activated.
Remarks
Override in derived class to respond to page activations.
Overview | Class members | Known problems
CTabCtrlSSL::OnDeactivatePage
void OnDeactivatePage (int nIndex, int nPageID);
Parameters
nIndex
Zero-based index of the tab page being activated.
nPageID
Zero-based page identifier of the page being activated.
Remarks
Override in derived class to respond to page deactivations.
Overview | Class members | Known problems
CTabCtrlSSL::OnDestroyPage
void OnDestroyPage (int nIndex, int nPageID);
Parameters
nIndex
Zero-based index of the tab page being activated.
nPageID
Zero-based page identifier of the page being activated.
Remarks
Override in derived class to free resources.
Overview | Class members | Known problems
As far as I know there aren't any, but then the advanced tab part hasn't been tested much. If you find any let me know.
Updates
- 01 Oct 2003
The most common question I have had about CTabCtrlSSL
is about x, y, or z control that someone can't receive messages for.
I have explained the problem and the solution many times to many people and now, with thanks to John A Johnson, I have a solution that really works and can be applied to the source code for all.
The problem was that WM_COMMAND
, WM_NOTIFY
and WM_CMDMSG
were passed on to the parent of the tab page, the tab control, which then passed them on to its parent (usually a dialog) so that the parent dialog could handle notifications, etc. of the tab pages. Unfortunately, this meant that any derived tab page would never get a chance to handle its own control notifications, unless you removed the OnCommand
, OnNotify
and OnCmdMsg
handlers in CTabPageSSL
.
The real solution is to turn off this command routing by default as most people want to handle control notifications in the class that owns them directly, but allow people to turn command routing back on, if they want the parent dialog to handle them instead. This resulted in a few extra methods in CTabPageSSL
. But if you only want to handle commands/notifications in your derived tab page, you won't need to change your code at all, just drop in the new TabPageSSL.h and TabPageSSL.cpp.