The BCGToolbar Library
This article is written as an extension to an excellent toolbar library written by Stas Levin. This article is written with version 5.2 of the library, but take a look at the BCGSoft website for the latest version. I will not get into how you use and install the library. This article describes how to add a custom page to the Customisation dialog box, which is displayed when you right click on a toolbar or menu and choose "Customize...”. The sample program is based on the sample "CustomPages", which comes with the library. Parts of the code are also based on the BCGSkins sample, which demonstrates how to add skins to your application with the BCGControlBar library. I will not describe the
CSkinPage class in detail, but rather describe how to use it.
Use an existing BCGControlBar application or create a new application using the application wizard. Copy the following files to your application directory from the sample project:
Also add the necessary resources from the .RC file.
The CSkinPage class
The page to be added is a
CPropertyPage derivative called
CSkinPage. It extends
CPropertyPage with the methods
void AddSkin(LPCSTR szName, LPCSTR szDescription, UINT nIDBitmapPreview);
The name of the skin as it is displayed in the list.
A short description of the skin, that is displayed below the list with available skins.
A bitmap resource to be displayed as a preview when the skin is selected from the list.
You may omit the preview by setting
nIDBitmapPreview to 0. This way no preview is displayed but you will still be able to see a real-time preview of the change in the actual application.
The bitmap resource should be 256 colors and is fully palette aware.
void SelectSkin(int iSkin);
1. Adding the custom page
CMainFrame::OnViewCustomize(), insert this code that will be executed when you choose to customize the toolbars and menus.
lstCustomPages.AddTail (RUNTIME_CLASS (CSkinPage));
CBCGToolbarCustomize* pDlgCust = new CBCGToolbarCustomize (this,
BCGCUSTOMIZE_MENU_SHADOWS | BCGCUSTOMIZE_TEXT_LABELS |
BCGCUSTOMIZE_LOOK_2000 | BCGCUSTOMIZE_MENU_ANIMATIONS, &lstCustomPages);
CMyBCGToolbarCustomize* pTemp = (CMyBCGToolbarCustomize*)pDlgCust;
CSkinPage* pSkinPage = DYNAMIC_DOWNCAST(CSkinPage,
pSkinPage->AddSkin("Office XP", "The new Windows look", IDB_PREVIEW2);
"That ol\' mac style. For people that just can't let go :-)",
pSkinPage->AddSkin("Stas on mushrooms",
"A weird and strange windows look, "
"that you propably won't stand for long", IDB_PREVIEW3);
int iSelectedSkin = theApp.GetInt("ActiveSkin", 0);
The interesting part is the last lines where you add the actual pages and select one of them as the active one. Notice that the active skin index is read from the registry - I will get back to that later.
You may see that I do a dirty cast from the
CBCGToolbarCustomize class that is created, to my own class
CMyBCgToolbarCustomize, which derives from
CBCGToolbarCustomize. The reason I do that, is because I need to access one of the protected members of the class. Since I'm not able to change the original source code of the library, I'm forced to do something like that.
If you insert this code and choose "Customize...", you will be able to see a new page among the well-known
BCGToolbar pages. You can play around with it and see that it actually displays a preview and a description for each entry in the list - but nothing much else happens.
2. Adding skin support
Add a new member function to you application class to take care of the skins. I have taken the
SetSkin() function from the BCGSkins sample that comes with the library, and modified it a bit.
void CCustomPagesApp::SetSkin(int iIndex)
if (CBCGVisualManager::GetInstance () != NULL)
delete CBCGVisualManager::GetInstance ();
new CMyVisualManager ();
new CMacStyle ();
new CFunnyStyle ();
CBCGVisualManager::GetInstance ()->SetLook2000 ();
CBCGVisualManager::GetInstance ()->RedrawAll ();
On the last line, the index of the last used skin is written to the registry. This is the value that is used in
When you use skins, you should be aware that you release any resources allocated on the fly. Refer to the BCGSkins sample for further information. Also make sure that the right header files are included so the application compiles.
In you application class
InitInstance(), you should add the following code just before showing the main window.
int iSelectedSkin = GetInt("ActiveSkin", 0);
This code will make sure that it is the right skin that is selected on start up.
Now we will connect the customisation page and the application class so the right skin will be activated. As an added bonus, we will get a real-time preview in the application window itself.
Each time you select another skin in the list, a registered Windows message is fired to the main window of the application. To handle this message, add a
ON_REGISTERED_MESSAGE() handler to the message map of
CMainFrame. I have called the function
OnNewSkin(). Below is the implementation.
LRESULT CMainFrame::OnNewSkin(WPARAM wp,LPARAM lp)
int iSkindex = wp;
CCustomPagesApp* pApp = (CCustomPagesApp*)AfxGetApp();
This function does not do anything interesting. The index of the selected skin comes in from the
WPARAM parameter and is transferred directly to the
SetSkin() function in the application class. Make sure that the order in which you add the skins to the list is exactly the same as the order in which the index is handled in
SetSkin() - starting from one, since the first entry always is "<no skin>".
That's it! Go ahead and try it out. If you are not familiar with the BCGControlBar library, go ahead and download a copy. The library is free to use for freeware development, and quite inexpensive for commercial applications, compared to similar libraries like the ones from Stingray and Dundas. Keep up the good work Stas!!
Please note that I'm not in any way connected to BCGSoft, other than I use the library in my daily work.