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.
Getting started
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:
- Macstyle.*
- Funnystyle.*
- MyVisualManager.*
- SkinPage.*
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 AddSkin()
and SelectSkin()
.
void AddSkin(LPCSTR szName, LPCSTR szDescription, UINT nIDBitmapPreview);
Return Value
none
Parameters
szName
The name of the skin as it is displayed in the list.
szDescription
A short description of the skin, that is displayed below the list with available skins.
nIDBitmapPreview
A bitmap resource to be displayed as a preview when the skin is selected from the list.
Remarks
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);
Return Value
none
Parameters
Remarks
none
1. Adding the custom page
In CMainFrame::OnViewCustomize()
, insert this code that will be executed when you choose to customize the toolbars and menus.
CList lstCustomPages;
lstCustomPages.AddTail (RUNTIME_CLASS (CSkinPage));
CBCGToolbarCustomize* pDlgCust = new CBCGToolbarCustomize (this,
TRUE ,
BCGCUSTOMIZE_MENU_SHADOWS | BCGCUSTOMIZE_TEXT_LABELS |
BCGCUSTOMIZE_LOOK_2000 | BCGCUSTOMIZE_MENU_ANIMATIONS,
&lstCustomPages);
pDlgCust->Create ();
CMyBCGToolbarCustomize* pTemp = (CMyBCGToolbarCustomize*)pDlgCust;
CSkinPage* pSkinPage = DYNAMIC_DOWNCAST(CSkinPage,
pTemp->GetCustomPageList()->GetHead());
pSkinPage->AddSkin("Office XP", "The new Windows look", IDB_PREVIEW2);
pSkinPage->AddSkin("Mac",
"That ol\' mac style. For people that just can't let go :-)",
IDB_PREVIEW1);
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);
pSkinPage->SelectSkin(iSelectedSkin);
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 ();
}
switch (iIndex)
{
case 0:
CBCGVisualManager::GetInstance ();
break;
case 1:
new CMyVisualManager ();
break;
case 2:
new CMacStyle ();
break;
case 3:
new CFunnyStyle ();
break;
}
CBCGVisualManager::GetInstance ()->SetLook2000 ();
CBCGVisualManager::GetInstance ()->RedrawAll ();
WriteInt("ActiveSkin", iIndex);
}
On the last line, the index of the last used skin is written to the registry. This is the value that is used in CMainFrame::OnViewCustomize()
.
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);
SetSkin(iSelectedSkin);
This code will make sure that it is the right skin that is selected on start up.
3. Connecting
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;
ASSERT(iSkindex>=0);
CCustomPagesApp* pApp = (CCustomPagesApp*)AfxGetApp();
ASSERT_VALID(pApp);
pApp->SetSkin(iSkindex);
return 0;
}
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.