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

Ribbon-style Two-level Tab Bar for Dialog Boxes in WTL

, 7 Apr 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
A simple, functional, and attractive windowless control to jazz up your dialog boxes
sample-dialogs.png

Introduction

I created this simple and attractive two-level tab bar for use exclusively in dialog boxes. It can be used to improve property pages and dialog-based applications.

This project was developed using Microsoft Visual C++ 2008. It requires WTL 8 and GDI+. It was tested on Windows XP, Windows Vista, and Windows 7 Beta 1.

Inspiring the Look

I took a screenshot of the Microsoft Office Word 2007 Ribbon bar and played with it in Paint to come up with a simplified Ribbon interface:

office-ribbon.png

But there are many examples of two-level tabs, so I put together some of my favorites:

inspiration.png

The things I like from the above are:

  • the look of the selected first-level tab (tab) in (1), (5), and (6)
  • the look of the selected second-level tab (sub-tab) in (4) and (6)
  • the icons in (1)
  • the gradient fills in (4)
  • the simple hot-tracking in (2) and (5)

But I also wanted:

  • the tabs on "glass" – at least on Vista and better. There should only be a background fill when the Desktop Window Manager (i.e., Aero) is not available.
  • the icon in the selected sub-tab only to be in color. The other icons could be recolored (specifically, toned) to the tab color – I call this, "color-washing" the icons.
  • to be able to change the colors easily.

Design Decisions

Windowless Control

The code extends the top of the non-client area by the height of the tab bar. The "sheet of glass" is extended onto this area. The tabs are drawn entirely on the non-client area.

Draw with GDI+

All of the drawing is done with the help of GDI+. I did not want to use bitmaps, because I wanted to be able to easily change the colors of my ribbon tab bar.

The only graphics needed are optional icons for the sub-tabs. Only full color icons are necessary. GDI+ is used to color-wash the icons (for unselected sub-tabs). Color-washing basically:

  1. converts the icon to grayscale; and
  2. adds color to the icon

It is accomplished easily with a ColorMatrix.

Animation

I wanted a subtle animation (dissolve/cross-fade) when the state changed, i.e., when an item was hot-tracked, or the tab or sub-tab selection changed. This is easily accomplished in Vista or better with BeginBufferedAnimation/EndBufferedAnimation.

Now, BeginBufferedAnimation expects you to draw the initial state and the final state in an animation, and it will take care of drawing the intermediate frames. That is what is done in the sample in the MSDN documentation for BeginBufferedAnimation. However, I discovered (quite by accident!) that if I delete the DC hdcFrom (that is, DeleteDC(hdcFrom)) returned by BeginBufferedAnimation, then Windows will auto-magically use what is already drawn as the initial state, so I only needed to draw the final state. That saved me from having to deal with caching or re-drawing an initial state every time the state changed.

Keyboard Control

The standard tab control can be controlled with the keyboard arrow keys. If the tab is in a property sheet, then there is some awkward (in my opinion) tabbing from the tabs back to the pages.

I wanted to keep it simple, so I decided that tabs should have access keys. For example, the tab "&Properties" is displayed, initially, without the "p" mnemonic ("Properties"). After the user presses the ALT key, the mnemonic is underlined (“Properties”). Pressing ALT+P will select the Properties tab. When Properties is the active tab, its "p" is no longer a mnemonic - after all, one cannot re-select an already selected tab. That means that "p" can be used again, either in another tab or a sub-tab. This functionality helps to reduce mnemonic collisions.

To get this functionality, all I needed to do was generate an accelerator table and then let TranslateAccelerator do its job (in PreTranslateMessage). The accelerator table is updated every time the selection changes.

Using the Code

Include RibbionDialog.h and derive a dialog box from CRibbonDialogImpl (instead of CDialogImpl). Then, add CHAIN_MSG_MAP(CRibbonDialogImpl<...>) to the beginning of your message map.

Adding Tabs and Sub-Tabs

To add a tab, just call AddTab with a string resource ID. To add a sub-tab, call AddSubTab with a string resource ID and an optional icon resource ID – the sub-tab will be added to the last tab. For example:

// Add a tab
AddTab(ID_TAB1);

// Add two sub-tabs to TAB1
AddSubTab(ID_TAB1SUBTAB1, IDI_ICON1);
AddSubTab(ID_TAB1SUBTAB2, IDI_ICON2);

// Add another tab
AddTab(ID_TAB2);

// Add three sub-tabs to TAB2
AddSubTab(ID_TAB2SUBTAB1, IDI_ICON3);
AddSubTab(ID_TAB2SUBTAB2, IDI_ICON4);
AddSubTab(ID_TAB2SUBTAB3, IDI_ICON5);

You have to add at least one tab, and every tab must have at least one sub-tab.

Handling the Notification of the Changed Selection

The string resource IDs also function as command identifiers. When a selection changes, you will receive a WM_COMMAND notification message. Use COMMAND_ID_HANDLER or MSG_WM_COMMAND in your message map to handle these messages:

BEGIN_MSG_MAP_EX(CMainDlg)
    CHAIN_MSG_MAP(CRibbonDialogImpl<CMainDlg>)
    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    ...
        COMMAND_ID_HANDLER(ID_TAB1SUBTAB1, OnTab1Subtab1Selected)
    MSG_WM_COMMAND(OnCommand)
    ...
END_MSG_MAP()

(The low-order word of wParam will be the string resource ID of the selected sub-tab. The high-order word of wParam will be 0.)

Changing the Colors

Just change the color constants in the header file to modify the look of your ribbon tab bar.

About the Sample Application

The sample application shows how the ribbon tab bar could be used in a re-sizable dialog box. When a dialog is re-sized, you'll notice that:

  1. there is no flicker; and
  2. animation is temporarily turned off

Final Remarks

I tried to follow the KISS principle, and I believe this project is, indeed, simple – but still functional and beautiful. I hope you think so, too. Smile | :)

History

  • Apr 6, 2009
    • Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Raj Pabari
Software Developer
Canada Canada
Raj collects postcards. He has been known to wear a bow tie on occasion. He's an optimist.
 
"Don't take life too seriously. You'll never get out alive." ~ Elbert Hubbard

Comments and Discussions

 
GeneralNice job PinmemberReza Jahanbakhshi4-Jun-09 23:40 

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 | Mobile
Web04 | 2.8.141015.1 | Last Updated 7 Apr 2009
Article Copyright 2009 by Raj Pabari
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid