Chapter 11

Toolbars and Status Bars


Building a good user interface is half the battle of programming a Windows application. Luckily, MFC supplies an amazing amount of help in creating an application that supports all of the expected user-interface elements, including menus, dialog boxes, toolbars, and status bars. The subjects of menus and dialog boxes are covered earlier in this book, in chapter 6, "Using Menus," and in chapter 7, "Programming Dialog Boxes." In this chapter, you learn how to get the most out of toolbars and status bars.

Working with Toolbars

Although you can add a toolbar to your application with AppWizard, you still need to use a little programming polish to get things just right. This is because every application is different and AppWizard can create only the most generally useful toolbar for most applications. When you create your own toolbars, however, you'll almost certainly want to add or delete buttons to support your application's unique command set.

For example, when you create a standard AppWizard application with a toolbar, AppWizard creates the toolbar shown in Figure 11.1. This toolbar provides buttons for the commonly used commands in the File and Edit menus, as well as a button for displaying the About dialog box. But what if your application won't support these commands or adds additional commands? It's up to you to modify the default toolbar to fit your application.


FIG. 11.1

The default toolbar provides buttons for commonly used commands.

The truth is that modifying an AppWizard-generated application's toolbar is an easy task, especially if you know how toolbars work. Because adding a toolbar to any application is also fairly easy, in this chapter you'll put together an application that creates and manipulates its toolbar without the help of AppWizard. After you've finished this chapter, you'll have no problem dealing with toolbars in AppWizard or non-AppWizard applications.

Introducing the Toolbar Application

Before you start digging into the details of toolbar creation and manipulation, take a look at the sample program, an application called Toolbar that you'll find in the Chap11\Toolbar1 folder at this book's Web site. When you run the application, you see the window shown in Figure 11.2. The toolbar sports three buttons. Just click a button to change the color of the rectangle displayed in the application's window.


FIG. 11.2

The Toolbar application has a three-button toolbar.

Another cool thing about the Toolbar application is the way that you can drag the toolbar from its resting place below the menu bar and drop it elsewhere on the screen, changing it into a toolbox (see Figure 11.3). Go ahead and try it. After you have the toolbox on the screen, drag it down to the bottom of the application's window. You can dock it there, as shown in Figure 11.4, creating a toolbar at the bottom of the window. (You can, of course, also dock the toolbar back at the top of the window, leaving it where it started.)


FIG. 11.3

The Toolbar application features a docking toolbar that can be changed into a toolbox.


FIG. 11.4

You can even dock the toolbar at the bottom of the window.

Finally, you can hide or display the toolbar by choosing the application's View, Toolbar command. The source code for the Toolbar application is shown in Listings 11.1 through 11.4.

Listing 11.1 toolbar.h The CToolbarApp Class's Header File

///////////////////////////////////////////////////////////
// TOOLBAR.H: Header file for the CToolbarApp class, which
//            represents the application object.
///////////////////////////////////////////////////////////
class CToolbarApp : public CWinApp
{
public:
    CToolbarApp();
    // Virtual function overrides.
    BOOL InitInstance();
};

Listing 11.2 toolbar.cpp The CToolbarApp Class's Implementation File

///////////////////////////////////////////////////////////
// TOOLBAR.CPP: Implementation file for the CToolbarApp,
//              class, which represents the application
//              object.
///////////////////////////////////////////////////////////
#include <afxwin.h>
#include "toolbar.h"
#include "mainfrm.h"
// Global application object.
CToolbarApp ToolbarApp;
///////////////////////////////////////////////////////////
// Construction/Destruction.
///////////////////////////////////////////////////////////
CToolbarApp::CToolbarApp()
{
}
///////////////////////////////////////////////////////////
// Overrides
///////////////////////////////////////////////////////////
BOOL CToolbarApp::InitInstance()
{
    m_pMainWnd = new CMainFrame();
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();
    return TRUE;
}

Listing 11.3 mainfrm.h The CMainFrame Class's Header File

///////////////////////////////////////////////////////////
// MAINFRM.H: Header file for the CMainFrame class, which
//            represents the application's main window.
///////////////////////////////////////////////////////////
#include <afxext.h>
enum {Red, Green, Blue};
class CMainFrame : public CFrameWnd
{
// Protected data members.
protected:
    CToolBar m_toolbar;
    int m_color;
// Constructor and destructor.
public:
    CMainFrame();
    ~CMainFrame();
// Overrides.
protected:
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// Message map functions.
public:
    afx_msg void OnPaint();
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnRed();
    afx_msg void OnGreen();
    afx_msg void OnBlue();
    afx_msg void OnViewToolbar();
    afx_msg void OnUpdateViewToolbarUI(CCmdUI* pCmdUI);
    DECLARE_MESSAGE_MAP()
};

Listing 11.4 mainfrm.cpp The CMainFrame Class's Implementation File

///////////////////////////////////////////////////////////
// MAINFRM.CPP: Implementation file for the CMainFrame
//              class, which represents the application's
//              main window.
///////////////////////////////////////////////////////////
#include <afxwin.h>
#include "mainfrm.h"
#include "resource.h"
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_CREATE()
    ON_WM_PAINT()
    ON_COMMAND(ID_RED, OnRed)
    ON_COMMAND(ID_GREEN, OnGreen)
    ON_COMMAND(ID_BLUE, OnBlue)
    ON_COMMAND(ID_VIEW_TOOLBAR, OnViewToolbar)
    ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, OnUpdateViewToolbarUI)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////
// CMainFrame: Construction and destruction.
///////////////////////////////////////////////////////////
CMainFrame::CMainFrame()
{
    // Create the main window. The WS_CLIPCHILDREN style
    // ensures that drawing in the window won't erase
    // parts of the toolbar.
    Create(NULL, "Toolbar App", WS_OVERLAPPEDWINDOW |
        WS_CLIPCHILDREN, rectDefault,
        NULL, MAKEINTRESOURCE(IDR_MENU1));
    // Set the initial rectangle color to red.
    m_color = Red;
}
CMainFrame::~CMainFrame()
{
}
///////////////////////////////////////////////////////////
// Overrides.
///////////////////////////////////////////////////////////
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    // Set size of the main window.
    cs.cx = 365;
    cs.cy = 440;
    // Call the base class's version.
    BOOL returnCode = CFrameWnd::PreCreateWindow(cs);
    return returnCode;
}
///////////////////////////////////////////////////////////
// Message map functions.
///////////////////////////////////////////////////////////
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // Call the base class's OnCreate().
    CFrameWnd::OnCreate(lpCreateStruct);
  
    // Create and load the toolbar.
    m_toolbar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP);
    m_toolbar.LoadToolBar(IDR_TOOLBAR1);
    // Set the toolbar's styles.
    DWORD styles = m_toolbar.GetBarStyle();
    styles |= CBRS_TOOLTIPS | CBRS_FLYBY;
    m_toolbar.SetBarStyle(styles);
    // Enable toolbar docking.
    m_toolbar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_toolbar);
    return 0;
}
void CMainFrame::OnPaint()
{
    CBrush* newBrush;
    CPaintDC paintDC(this);
    // Create the approproiate color brush.
    if (m_color == Red)
        newBrush = new CBrush(RGB(255,0,0));
    else if (m_color == Green)
        newBrush = new CBrush(RGB(0,255,0));
    else
        newBrush = new CBrush(RGB(0,0,255));
    // Select the new brush into the DC.
    CBrush* oldBrush = paintDC.SelectObject(newBrush);
    // Draw the rectangle.
    paintDC.Rectangle(50, 70, 300, 340);
    // Restore the DC and delete the new brush.
    paintDC.SelectObject(oldBrush);
    delete newBrush;
}
void CMainFrame::OnRed() 
{
    // Set the selected color to Red
    // and redraw the window.
    m_color = Red;
    Invalidate();
}
void CMainFrame::OnGreen() 
{
    // Set the selected color to Green
    // and redraw the window.
    m_color = Green;
    Invalidate();
}
void CMainFrame::OnBlue() 
{
    // Set the selected color to Blue
    // and redraw the window.
    m_color = Blue;
    Invalidate();
}
void CMainFrame::OnViewToolbar()
{
    // Determine whether the toolbar is visible.
    BOOL visible = m_toolbar.GetStyle() & WS_VISIBLE;
    // Show or hide the toolbar.
    ShowControlBar(&m_toolbar, !visible, FALSE);
}
void CMainFrame::OnUpdateViewToolbarUI(CCmdUI* pCmdUI)
{
    // Determine whether the toolbar is visible.
    BOOL visible = m_toolbar.GetStyle() & WS_VISIBLE;
    // Check or uncheck the Toolbar menu item.
    pCmdUI->SetCheck(visible);
}

Creating the Toolbar Resource

The first step in adding a toolbar to an application is to create the toolbar's resource. You can do this easily by using Developer Studio's toolbar editor. To create a toolbar resource, load or start a project and complete the following steps.

1. Choose the Insert, Resource command from Developer Studio's menu bar. The Insert Resource dialog box appears, as shown in Figure 11.5.


FIG. 11.5

You can choose a type of resource from the Insert resource dialog box.

2. In the Resource Type box, choose Toolbar, and then click New. Developer Studio creates a blank toolbar and loads it into the toolbar editor (see Figure 11.6).


FIG. 11.6

Developer Studio's toolbar editor enables you to customize your application's toolbar

3. Use the toolbar editor's tools to draw an icon on the blank toolbar button. When you do, a new toolbar button appears next to the button on which you drew an icon, as shown in Figure 11.7.


FIG. 11.7

A new button appears when you draw on the original blank button.

4. After drawing the button's icon, double-click the button's image in the toolbar. The Toolbar Button Properties sheet appears.

5. Type the button's resource ID in the ID box, and type the button's prompt text in the Prompt box, as shown in Figure 11.8.

The prompt is comprised of two strings separated by a newline character (\n). The first string is the hint text that appears in the application's status bar (if the application has a status bar), and the second string is the text for the button's tool tip, which appears whenever the user holds the mouse pointer over the button for a second or two.


FIG. 11.8

The Toolbar Button Properties sheet enables you to set a button's ID and prompt.

6. Repeat steps 3 through 5 for each button that you want to add to the toolbar.

Creating Toolbar Message Response Functions

Now that you have created your toolbar buttons, you have to tell the application what to do when the user chooses one of the buttons. You do this by associating, in the application's message map, a message response function with each button's resource ID.

See “Responding to Windows Messages,” for more information on message maps, (ch. 4)

Adding a message response function for a toolbar button is almost exactly like adding one for a menu item. First, you place an ON_COMMAND macro in the message map for each toolbar button. Such an entry looks like this:

ON_COMMAND(ID_RED, OnRed)

The two arguments for the ON_COMMAND message map macro are the command's ID and the name of the function that responds to a message with the given ID. In the example here, whenever the user clicks the toolbar button with the ID_RED ID, the OnRed() message response function is called.

In the message response function, you write the program lines that will perform the actions necessary to respond to the command. In the case of the ID_RED button, this means drawing a red rectangle in the window, as shown in Listing 11.5.

Listing 11.5 lst11_05.cpp Responding to a Toolbar Button

void CMainFrame::OnRed() 
{
    // Set the selected color to Red
    // and redraw the window.
    m_color = Red;
    Invalidate();
}

In Listing 11.5, the program sets the member variable m_color to indicate the selected color. Then a call to Invalidate() causes the window to be redrawn. In the program's OnPaint() function, the program uses m_color to set the appropriate color before drawing the rectangle, as shown in Listing 11.6.

See “Painting in an MFC Program,” for more information on drawing in a window, (ch. 5)

Listing 11.6 lst11_06.cpp Drawing the Rectangle with the Selected Color

void CMainFrame::OnPaint()
{
    CBrush* newBrush;
    CPaintDC paintDC(this);
    // Create the approproiate color brush.
    if (m_color == Red)
        newBrush = new CBrush(RGB(255,0,0));
    else if (m_color == Green)
        newBrush = new CBrush(RGB(0,255,0));
    else
        newBrush = new CBrush(RGB(0,0,255));
    // Select the new brush into the DC.
    CBrush* oldBrush = paintDC.SelectObject(newBrush);
    // Draw the rectangle.
    paintDC.Rectangle(50, 70, 300, 340);
    // Restore the DC and delete the new brush.
    paintDC.SelectObject(oldBrush);
    delete newBrush;
}

If you haven't defined a message response function for a toolbar button, MFC disables the button when you run the application. This is also true for menu commands that have not yet been associated with a message response function. In fact, for all intents and purposes, toolbar buttons are menu commands.

Ordinarily, toolbar buttons duplicate menu commands, providing a quicker way for the user to select commonly used commands in the menus. In this case, the menu item and the toolbar button both represent the exact same command and you give both the same ID. Then, the same message response function is called whether or not the user selects the command from the menu bar or from the toolbar.

Creating and Displaying a Toolbar

At this point, you know how to create a toolbar resource, as well as how to respond to the toolbar buttons in your program. You have only one tiny problem. You haven't yet displayed the toolbar in your application's window, which makes it mighty tough for the user to click one of its buttons!

You create your toolbar in the window class's OnCreate() function, which, after you add the ON_WM_CREATE macro to the window's message map, MFC calls in response to the Windows message WM_CREATE. The first step is to create an object of the MFC CToolBar class, like this:

m_toolbar.Create(this);

Here, m_toolbar is a data member of the CMainFrame window class. You call the toolbar object's Create() function with at least one argument, which is a pointer to the parent window. The Create() function, however, actually requires three arguments, two of which have default values. The function's signature looks like this:

BOOL Create( CWnd* pParentWnd, DWORD dwStyle =
    WS_CHILD | WS_VISIBLE | CBRS_TOP, UINT nID = AFX_IDW_TOOLBAR );

As you can see, the second argument is the toolbar's window styles, and the third argument is the toolbar's ID, which is important in applications that have multiple toolbars. (The application does, after all, need some way to tell one toolbar from another.) The default styles create a normal child-window toolbar that's positioned at the top of the parent window. You can, however, use other toolbar styles, which are described in Table 11.1. (The default toolbar ID is used internally by MFC and doesn't affect the ID you gave the toolbar when you created its resources.)

Table 12.1 Toolbar Styles

Style Description
CBRS_BOTTOM The toolbar is placed at bottom of the frame window.
CBRS_FLOATING The toolbar is displayed in a floating window.
CBRS_FLYBY The application's status bar can display the button's description.
CBRS_HIDE_INPLACE The toolbar is hidden from the user.
CBRS_NOALIGN The toolbar will not be repositioned when the parent window is resized.
CBRS_SIZE_DYNAMIC The toolbar can be resized.
CBRS_SIZE_FIXED The toolbar cannot be resized.
CBRS_TOOLTIPS The toolbar can display tool tips.
CBRS_TOP The toolbar is placed at top of the frame window.

After creating the CToolBar object, the program must load the toolbar from the application's resources, like this:

m_toolbar.LoadToolBar(IDR_TOOLBAR1);

The LoadToolBar() function's single argument is the toolbar's resource ID. In this example, the toolbar uses the default ID suggested by the Developer Studio when the toolbar resource was created.

Because the CToolBar class is declared in the AFXEXT.H file, you must add the line #include <afxext.h> to any file that accesses the CToolBar class. This is also true for status bars, which are represented by the MFC class CStatusBar.

Setting the Toolbar's Styles

After you call the toolbar's Create() and LoadToolBar() functions, the toolbar is displayed on the screen, fully functional. However, you might still want to tinker a bit with how the toolbar looks and acts. For example, the Toolbar sample program displays tool tips for the toolbar buttons. To enable tool tips, you have to change the toolbar's default style. You can add the toolbar styles that you want when you call Create() to create the toolbar, like this:

m_toolbar.Create(this, WS_CHILD | WS_VISIBLE |
    CBRS_TOP | CBRS_TOOLTIPS);

Another way, however, is to use the toolbar's member functions to change its styles after the toolbar has been created. This is the method used by the Toolbar example program. The first step is to get the styles that are already assigned to the toolbar. You do this by calling the toolbar object's GetBarStyle() function:

DWORD styles = m_toolbar.GetBarStyle();

This function returns a double word containing the toolbar's current styles. You can then add whatever styles you like by ORing the new styles with the styles returned by GetBarStyle(), like this:

styles |= CBRS_TOOLTIPS | CBRS_FLYBY;

This line adds the CBRS_TOOLTIPS and CBRS_FLYBY styles to the toolbar's current styles that were returned in styles. The CBRS_TOOLTIPS style enables the toolbar to display a tool tip whenever the user places the mouse pointer over the toolbar button for a second or two, as shown in Figure 11.9. The CBRS_FLYBY style enables the application's status bar (of which the Toolbar application currently has none, but will later in this chapter, in the section "Working with Status Bars") to display a description of the button's command when the mouse pointer passes over the button.


FIG. 11.9

Tool tips appear when the user places the mouse pointer over a toolbar button.

After adding the styles that you want, you hand the styles back to the toolbar by calling the toolbar object's SetBarStyle() function, like this:

m_toolbar.SetBarStyle(styles);

This function's single argument is the double word containing the toolbar's style flags.

Enabling Toolbar Docking

One of the advanced features of an MFC toolbar is its capability to be docked and undocked from its window. When the user drags the toolbar from the top of the window and drops it elsewhere on the screen, the toolbar becomes a toolbox, which is a floating window containing the toolbar buttons. The user can also dock the toolbar back into the window wherever the application permits, usually at the top or bottom of the window. The user can choose the exact position of the toolbar's docking place: to the left, middle, right, or anywhere in between. Figure 11.10, for example, shows the Toolbar application's toolbar docked in the center of the upper portion of the window.


FIG. 11.10

A dockable toolbar can be placed just about anywhere in the parent window.

To enable toolbar docking, you must first call the toolbar object's EnableDocking() function, like this:

m_toolbar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);

The function's single argument is the styles that indicate where the toolbar can be docked. These styles can be any combination of CBRS_ALIGN_TOP, CBRS_ALIGN_BOTTOM, CBRS_ALIGN_LEFT, CBRS_ALIGN_RIGHT, and CBRS_ALIGN_ANY.

After you've enabled docking for the toolbar, you must do the same for the window in which the toolbar will be docked:

EnableDocking(CBRS_ALIGN_ANY);

The window object's EnableDocking() function requires similar style flags. In this case, howver, you're determining not where any specific toolbar can be docked, but where any toolbar can be docked. This is a handy system whenever you want more than one toolbar. Each toolbar can be restricted to a given area, where the given area for all toolbars is determined by the window. Here's an example:

m_toolbar1.EnableDocking(CBRS_ALIGN_TOP);
m_toolbar2.EnableDocking(CBRS_ALIGN_BOTTOM);
EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);

Here, toolbar 1 can be docked only at the top of the window, whereas toolbar 2 can be docked only at the bottom of the window. The window must enable docking on both the top and bottom of the window.

After enabling docking for both the toolbar and the window, the last step is to actually dock the toolbar by calling the window's DockControlBar() function, like this:

DockControlBar(&m_toolbar);

The DockControlBar() function's single argument is the address of the toolbar object to dock.

If you fail to call DockControlBar() for a dockable toolbar, the user will be unable to undock or dock the toolbar.

Showing and Hiding the Toolbar

Like most applications with toolbars, in the Toolbar example application, the program enables the user to hide the toolbar in order to have more window space. The user can hide the toolbar by choosing the application's View, Toolbar command from the menu bar. To respond to this command, the program has a OnViewToolbar() message response function. The first thing that the program must do in this function is determine whether the toolbar is currently visible or hidden. A call to toolbar object's IsVisible() member function (inherited from the CControlBar class) provides this information:

BOOL visible = m_toolbar.IsVisible();

The IsVisible() function returns TRUE if the toolbar is currently displayed and returns FALSE if the toolbar is hidden. All the application must do now is call the toolbar's ShowControlBar() function to hide or show the toolbar as appropriate:

ShowControlBar(&m_toolbar, !visible, FALSE);

This function's three arguments are

As you can see in the previous line of code, the program displays or hides the toolbar by using the NOT operator on the Boolean value returned from IsVisible(). The NOT operator reverses the Boolean value so that if the toolbar is already displayed, the toolbar will be hidden, and vice versa.

You might have noticed that the View, Toolbar command of your application is checked when the toolbar is visible and unchecked when it's hidden. This is done by creating a command UI function for the command, as shown in Listing 11.7. (For more information on command UI functions, please refer back to chapter 6, "Using Menus.")

Listing 11.7 lst11_07.cpp The Toolbar Command's Command UI Function

void CMainFrame::OnUpdateViewToolbarUI(CCmdUI* pCmdUI)
{
    // Determine whether the toolbar is visible.
    BOOL visible = m_toolbar.IsVisible();
    // Check or uncheck the Toolbar menu item.
    pCmdUI->SetCheck(visible);
}

The OnUpdateViewToolbarUI() function calls IsVisible() to determine the toolbar's visible state. The function then uses the returned Boolean value as the SetCheck() function's single argument. This places a checkmark next to the command when the toolbar is visible and removes the checkmark when the toolbar is hidden.

Creating Toolbar Radio Buttons

When you come right down to it, the buttons on the Toolbar application's toolbar should work like radio buttons. That is, because the buttons determine the state of the rectangle in the window a mutually exclusive state in which the rectangle must always be one and only one of three colors they should reflect that state by leaving the currently selected color's button depressed. Then, when the user selects a new color, the old color's button should pop up, and the new color's button should remain depressed.

The Toolbar2 application, which you'll find in the Chap11\Toolbar2 folder at this book's Web site, adds this functionality to the program's toolbar. Figure 11.11 shows the application when it's first run. Because the rectangle begins red, the toolbar's red button is depressed. That button stays depressed until the user selects a new color.


FIG. 11.11

The Toolbar2 application features radio-style buttons in its toolbar.

To create a radio-button toolbar, you use the class data member that tracks the currently selected button. As you know, the Toolbar2 application does this by creating an enumeration containing the possible button states, like this:

enum {Red, Green, Blue};

The application then declares a data member in which to store the currently selected color:

int m_color;

So that the program starts off with the correct button depressed, the window class's constructor sets this new member variable to its starting value:

m_color = Red;

In the toolbar buttons' message response functions, the program changes the value of m_color as appropriate.

The value of the m_color variable is used in a set of command UI functions created for each button, as shown in Listing 11.8:

Listing 11.8 lst11_08.cpp Setting the Radio-Button State

void CMainFrame::OnUpdateRedUI(CCmdUI* pCmdUI)
{
    // Press or unpress the toolbar button.
    pCmdUI->SetCheck(m_color == Red);
}
void CMainFrame::OnUpdateGreenUI(CCmdUI* pCmdUI)
{
    // Press or unpress the toolbar button.
    pCmdUI->SetCheck(m_color == Green);
}
void CMainFrame::OnUpdateBlueUI(CCmdUI* pCmdUI)
{
    // Press or unpress the toolbar button.
    pCmdUI->SetCheck(m_color == Blue);
}

In this case, the SetCheck() function doesn't create a checkmark, but rather depresses the selected toolbar button and unpresses the other buttons.

To modify the Toolbar1 application so that its toolbar uses radio buttons, perform the following steps:

ON THE WEB

http://www.quecorp.com/semfc The complete source code and executable file for the Toolbar2 application can be found in the Chap11\Toolbar2 directory at this book's Web site.

1. Load mainfrm.h, and add the following lines to the message map function declarations, right after the line afx_msg void OnUpdateViewToolbarUI(CCmdUI* pCmdUI):

afx_msg void OnUpdateRedUI(CCmdUI* pCmdUI);

afx_msg void OnUpdateGreenUI(CCmdUI* pCmdUI);

afx_msg void OnUpdateBlueUI(CCmdUI* pCmdUI);

2. Load mainfrm.cpp, and add the following lines to the class's message map, right after the line ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, OnUpdateViewToolbarUI):

ON_UPDATE_COMMAND_UI(ID_RED, OnUpdateRedUI)

ON_UPDATE_COMMAND_UI(ID_GREEN, OnUpdateGreenUI)

ON_UPDATE_COMMAND_UI(ID_BLUE, OnUpdateBlueUI)

3. Add the functions shown in Listing 11.9 to the end of the mainfrm.cpp file.

Listing 11.9 lst11_09.cpp New UI Functions for the Toolbar Application

void CMainFrame::OnUpdateRedUI(CCmdUI* pCmdUI)
{
    // Press or unpress the toolbar button.
    pCmdUI->SetCheck(m_color == Red);
}
void CMainFrame::OnUpdateGreenUI(CCmdUI* pCmdUI)
{
    // Press or unpress the toolbar button.
    pCmdUI->SetCheck(m_color == Green);
}
void CMainFrame::OnUpdateBlueUI(CCmdUI* pCmdUI)
{
    // Press or unpress the toolbar button.
    pCmdUI->SetCheck(m_color == Blue);
}

You've now completed the second version of the Toolbar application. Choose Developer Studio's Build, Build command to compile and link the application. Then, choose Build, Execute (or just press Ctrl+F5) to run the application.

Creating Custom Toolbars

Normally, the toolbars that you create with MFC support only button controls. You know from using other Windows applications, however, that a toolbar can display many types of controls. For example, Word for Windows creates toolbars that contain several drop-down lists for selecting things like fonts and paragraph styles. You can create such custom toolbars by writing your own toolbar class. This is easier than you think, because you can derive the new class from MFC's CToolBar class, and then just add the extra features to the class that you need.

The third version of the Toolbar application demonstrates this powerful programming technique. Perform the following steps to modify the Toolbar application so that it uses a custom toolbar.

ON THE WEB

www://http.quecorp.com/semfc The complete source code and executable file for the Toolbar3 application can be found in the Chap11\Toolbar3 directory on this book's Web site.

1. Copy the files MyToolbar.h and MyToolbar.cpp to the Toolbar application's project directory.

2. Use Developer Studio's Project, Add to Project, Files command to add the MyToolbar.cpp file to the project's other files.

3. Load mainfrm.h, and add the following line near the top of the file, right after the line #include <afxext.h>:

#include "mytoolbar.h"

4. While still in mainfrm.h, change the line CToolBar m_toolbar to the following:

CMyToolbar m_toolbar;

5. Add the following line to mainfrm.h, right after the line int m_color:

CString m_displayStr;

6. Add the following lines to mainfrm.h right after the line virtual BOOL PreCreateWindow(CREATESTRUCT& cs):

// Local member functions.

public:

void ShowSelection(LPCSTR str);

7. Load mainfrm.cpp, and add the following lines to the end of the class's constructor:

// Set the display string.

m_displayStr = "Item 1";

8. Replace all the code in the body of the class's OnCreate() function with the lines shown in Listing 11.10:

Listing 11.10 lst11_10.cpp New Code for the Window's OnCreate() Function

    // Call the base class's OnCreate().
    CFrameWnd::OnCreate(lpCreateStruct);
  
    // Create and load the toolbar.
    m_toolbar.Create(this);
    // Enable the window for docking.
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_toolbar);
    return 0;

9. Add the following lines to the end of the OnPaint() function:

// Show the display string.

paintDC.TextOut(152, 150, m_displayStr);

10. Add the function shown in Listing 11.11 to the end of the mainfrm.cpp file.

Listing 11.11 lst11_11.cpp The New ShowSelection() Member Function

void CMainFrame::ShowSelection(LPCSTR str)
{
    m_displayStr = str;
    Invalidate();
}

11. Start Developer Studio's toolbar editor, and add a placeholder button to the toolbar, as shown in Figure 11.12. (The placeholder button can have any ID, and you can draw anything as the button's icon.) The placeholder button reserves space for the combo box control.


FIG. 11.12

You need to add a placeholder button to the toolbar.

You've now completed the third version of the Toolbar application. Choose Developer Studio's Build, Build command to compile and link the application. Then, choose Build, Execute (or just press Ctrl+F5) to run the application. When you do, you see the window shown in Figure 11.13. The window, not only displays a rectangle of the selected color, but also the string that's currently selected in the toolbar's combo box. To change the displayed string, just select a new item in the combo box.


FIG. 11.13

The third version of the Toolbar application features a custom toolbar.

Exploring the Custom Toolbar Class

The toolbar displayed in the new version of the toolbar application is very much like the standard MFC toolbar you've been learning about in this chapter. The only difference is that the new toolbar includes a combo box, as well as buttons. How is this bit of MFC magic performed? Listings 11.12 and 11.13 show the custom toolbar class's header and implementation files.

Listing 11.12 MyToolbar.h The CMyToolbar Class's Header File

///////////////////////////////////////////////////////////
// MYTOOLBAR.H: Header file for the CMyToolbar class,
//              which represents the application's custom
//              toolbar.
///////////////////////////////////////////////////////////
#ifndef __MYTOOLBAR_H
#define __MYTOOLBAR_H
#include <afxext.h>
class CMyToolbar : public CToolBar
{
// Protected data members.
protected:
    CComboBox m_comboBox;
// Constructor and destructor.
public:
    CMyToolbar();
    ~CMyToolbar();
// Message map functions.
public:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnComboBox();
    DECLARE_MESSAGE_MAP()
};
#endif

Listing 11.13 MyToolbar.cpp The CMyToolbar Class's Implementation File

///////////////////////////////////////////////////////////
// MYTOOLBAR.CPP: Implementation file for the CMyToolbar
//                class, which represents the application's
//                toolbar.
///////////////////////////////////////////////////////////
#include <afxwin.h>
#include "mytoolbar.h"
#include "resource.h"
#include "mainfrm.h"
BEGIN_MESSAGE_MAP(CMyToolbar, CToolBar)
    ON_WM_CREATE()
    ON_CBN_SELCHANGE(112, OnComboBox)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////
// CMainFrame: Construction and destruction.
///////////////////////////////////////////////////////////
CMyToolbar::CMyToolbar()
{
}
CMyToolbar::~CMyToolbar()
{
}
///////////////////////////////////////////////////////////
// Message map functions.
///////////////////////////////////////////////////////////
int CMyToolbar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // Call the base class's OnCreate().
    CToolBar::OnCreate(lpCreateStruct);
    // Load the toolbar resource.
    LoadToolBar(IDR_TOOLBAR1);
    // Change button placeholder into a separator.
    SetButtonInfo(3, 112, TBBS_SEPARATOR, 100);
    // Set the toolbar's styles and enable docking.
    DWORD styles = GetBarStyle();
    styles |= CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC;
    SetBarStyle(styles);
    EnableDocking(CBRS_ALIGN_ANY);
    // Calculate the combo box's coordinates.
    CRect rect;
    GetItemRect(3, &rect);
    rect.left += 5;
    rect.right -= 5;
    rect.bottom = rect.top + 100;
    // Create and initialize the combo box.
    m_comboBox.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | 
        CBS_SORT | CBS_DROPDOWNLIST, rect, this, 112);
    m_comboBox.AddString("Item 1");
    m_comboBox.AddString("Item 2");
    m_comboBox.AddString("Item 3");
    m_comboBox.AddString("Item 4");
    m_comboBox.SetCurSel(0);
    return 0;
}
void CMyToolbar::OnComboBox()
{
    // Get the index of the current selection.
    int index = m_comboBox.GetCurSel();
    // Get the selected string.
    char str[25];
    m_comboBox.GetLBText(index, str);
    // Give the selected string to the main window.
    CMainFrame* mainWindow = (CMainFrame*)AfxGetMainWnd();
    mainWindow->ShowSelection(str);
}

As you can see in Listing 11.12, the custom toolbar class is derived from MFC's CToolBar class, which means that the new CMyToolbar class inherits all the power of the original class. All that's left to do is add the features that you need in the new class. Specifically, you need to add the combo box and a way of responding to the combo box.

When you add a control such as a combo box to your custom toolbar, you must be sure to position a placeholder button on the toolbar. Otherwise, the toolbar that MFC creates for you will not have room for the new control. You add the placeholder control to the toolbar using Developer Studio's toolbar editor as you did when you modified the Toolbar application in the previous section "Creating Custom Toolbars."

The combo box, which is an object of MFC's CComboBox class, is a data member of the CMyToolbar class. In addition, the new toolbar class declares a message map function called OnComboBox(). This function responds whenever the user makes a selection in the combo box.

The application adds the combo box to the toolbar in the toolbar class's OnCreate() function. In fact, the CMyToolbar class takes over all of the toolbar creation tasks from the CMainFrame class, loading the toolbar resource and setting the toolbar's styles. To add a combo box to the toolbar, OnCreate() first changes the placeholder button into a separator:

SetButtonInfo(3, 112, TBBS_SEPARATOR, 100);

The toolbar's SetButtonInfo() function takes four arguments:

The next step is to get the coordinates of the separator:

CRect rect;
GetItemRect(3, &rect);

The toolbar object's GetItemRect() function retrieves the size and position of an item on the toolbar. The two arguments are the index of the item and the address of a CRect object in which to store the coordinates.

The OnCreate() function then converts the returned item coordinates to the coordinates needed for the combo box, like this:

rect.left += 5;
rect.right -= 5;
rect.bottom = rect.top + 100;

These calculations ensure that the control is properly spaced in the toolbar and that the combo box's height is large enough to accommodate its drop-down list.

Finally, OnCreate() creates the combo box control, adds strings to the control's list, and sets the first item as the currently selected item, as shown in Listing 11.14.

Listing 11.14 lst11_14.cpp Creating and Initializing the Combo Box

m_comboBox.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | 
    CBS_SORT | CBS_DROPDOWNLIST, rect, this, 112);
m_comboBox.AddString("Item 1");
m_comboBox.AddString("Item 2");
m_comboBox.AddString("Item 3");
m_comboBox.AddString("Item 4");
m_comboBox.SetCurSel(0);

After the toolbar's combo box is created and displayed, the user can manipulate it to make a new selection from the listed items. To respond to such a selection, the toolbar's message map must contain the ON_CBN_SELCHANGE macro, which maps the selection-change action to the OnComboBox() function, like this:

ON_CBN_SELCHANGE(112, OnComboBox)

The ON_CBN_SELCHANGE macro requires two arguments, which are the control's ID and the member function to call.

After you have added this line to the toolbar's message map, when the user changes the selection in the combo box, MFC calls OnComboBox(). In that function, the program first acquires the index of the newly selected item:

int index = m_comboBox.GetCurSel();

The program uses the returned index to get the string that represents the selected item, like this:

char str[25];
m_comboBox.GetLBText(index, str);

Finally, the program gets a pointer to the main window and calls the window's local ShowSelection() member function through that pointer:

CMainFrame* mainWindow = (CMainFrame*)AfxGetMainWnd();
mainWindow->ShowSelection(str);

The main window's ShowSelection() member function sets the window's display string (m_displayStr) to the selected string and calls Invalidate() to force the window to redraw its display with the new data.

Working with Status Bars

Status bars are mostly benign objects that sit at the bottom of your application's window, doing whatever MFC instructs them to do. This consists of displaying command descriptions and showing the status of various keys on the keyboard, including the Caps Lock and Scroll Lock keys. In fact, status bars are so mundane from the programmer's point of view that they aren't even represented by a resource that you can edit like a toolbar.

Still, a status bar, just like a toolbar, must reflect the interface needs of your specific application. For that reason, MFC's CStatusBar class features a set of methods with which you can customize the status bar's appearance and operation. Table 11.2 lists the methods along with brief descriptions.

Table 12.2 Methods of the CStatusBar class.

Method Description
CommandToIndex() Gets an indicator's index, given its ID
Create() Creates the status bar
GetItemID() Gets an indicator's ID, given its index
GetItemRect() Gets an item's display rectangle, given its index
GetPaneInfo() Gets information about an indicator
GetPaneStyle() Gets an indicator's style
GetPaneText() Gets an indicator's text
GetStatusBarCtrl() Gets a reference to the CStatusBarCtrl object represented by the CStatusBar object
SetIndicators() Sets the indicators' IDs
SetPaneInfo() Sets the indicators' ID, width, and style
SetPaneStyle() Sets an indicator's style
SetPaneText() Sets an indicator's text

When you create a status bar as part of an AppWizard application, a window similar to that shown in Figure 11.14 appears. The status bar has several parts, called panes, that display certain information about the status of the application and the system. These panes, which are marked in Figure 11.14, include indicators for the Caps Lock, Num Lock, and Scroll Lock keys, as well as a message area for showing status text and command descriptions. To see a command description, place your mouse pointer over a button on the toolbar (see Figure 11.15).


FIG. 11.14

The default MFC status bar contains a number of informative panes.


FIG. 11.15

The message area is used mainly for command descriptions

Currently, the Toolbar application you've been building in this chapter features no status bar. Although Toolbar is not an AppWizard generated program, you can still add a status bar. Perform the following steps to add a status bar to the application:

ON THE WEB

http://www.quecorp.com/semfc The complete source code and executable file for the Toolbar4 application can be found in the Chap11\Toolbar4 directory on this book’s Web site.

1. Load mainfrm.h, and add the following line to the class's data member declarations, right after the line CString m_displayStr, which you placed there previously.

CStatusBar m_statusBar;

2. Load mainfrm.cpp, and add the lines in Listing 11.15 right after the class's message map:

Listing 11.15 lst11_15.cpp Array of Status Bar Indicators

static UINT indicators[] =
{
    ID_SEPARATOR,
    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
};

3. Add the following lines near the end of the OnCreate() function, right before the line return 0:

// Create the status bar.

m_statusBar.Create(this);

m_statusBar.SetIndicators(indicators, 4);

4. Use the string table editor to add three strings to the application's string table. The three string IDs should be ID_INDICATOR_CAPS, ID_INDICATOR_NUM, and ID_INDICATOR_SCRL. The three strings associated with the IDs should be "CAPS", "NUM", and "SCRL", respectively, as shown in Figure 11.16.


FIG. 11.16

Adding indicator strings to the application's string table.

You've now completed the fourth version of the Toolbar application. Choose Developer Studio's Build, Build command to compile and link the application. Then, choose Build, Execute (or just press Ctrl+F5) to run the application. When you do, the window shown in Figure 11.17 appears. The window now boasts a nifty status bar. Press your keyboard's Caps Lock, Scroll Lock, and Num Lock keys to see the status bar's indicators turn on and off. Also, place your mouse pointer over one of the toolbar buttons, and its description appears in the status bar's message area.


FIG. 11.17

The sample application now has a status bar.

Understanding Status Bar Basics

The status bar that you created in the previous section is identical to the one that AppWizard creates for you when you use AppWizard to generate an application. Still, it's a snap to add to your application. The first thing you must do is decide what types of panes you want to include in the status bar. The standard status bar has panes for showing the status of the Caps Lock, Num Lock, and Scroll Lock keys, as well as a separator pane that's used to display command descriptions and other types of messages.

After you've determined the types of panes that you want to include in the status bar, you must create an array that contains IDs for each of the panes, as shown in Listing 11.16:

Listing 11.16 lst11_16.cpp Creating an Array of Indicator IDs

static UINT indicators[] =
{
    ID_SEPARATOR,
    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
};

MFC defines a set of default IDs that automatically link certain keyboard keys to status indicators as well as define separator panels. Listing 11.16 uses four of those predefined IDs in the indicator array. Other indicator IDs you can use are ID_INDICATOR_OVR, ID_INDICATOR_KANA, ID_INDICATOR_EXT, and ID_INDICATOR_REC.

After creating the indicator array, you can create the status bar. You do this in the main window's OnCreate() function, where you also create the window's toolbar:

m_statusBar.Create(this);

The status bar's Create() function takes a pointer to the parent window as its argument. You can also specify styles when you call Create(). You'll rarely need to do this, however, because the default styles work just fine for virtually every status bar.

After you create the status bar, you define its panes by calling the status bar object's SetIndicators() member function, like this:

m_statusBar.SetIndicators(indicators, 4);

The SetIndicators() function takes two arguments, which are the name of the ID array and the number of elements in the array.

Creating Custom Panes

If you use the predefined IDs for your status bar's panes, you need do nothing more than specify the IDs in the ID array. When the status bar appears in your application's window, MFC takes care of the rest. However, you might sometimes want to add your own panes to a status bar. To add a custom pane to a status bar, you must complete these steps:

1. Create a command ID for the new pane.

2. Create a default string for the pane.

3. Add the pane's command ID to the status bar's indicators array.

4. Create a command-update handler for the pane.

The following sections cover these steps in detail.

Creating a New Command ID

This step is easy, thanks to Developer Studio's symbol browser. To add the command ID, first choose the View, Resource Symbols command on Developer Studio's menu bar. When you do, you see the Resource Symbols dialog box (see Figure 11.18), which displays the currently defined symbols for your application's resources. Click the New button, and the New Symbol dialog box appears. Type the new ID into the Name box, and the IDs value into the Value box (see Figure 11.19). Usually, you can just accept the value that MFC suggests for the ID.


FIG. 11.18

Use the Resource Symbols dialog box to add new command IDs to your application.


FIG. 11.19

Type the new ID's name and value into the New Symbol dialog box.

Click the OK and Close buttons to finalize your selections, and your new command ID is defined.

Creating the Default String

The Visual C++ compiler insists that every status bar pane has a default string defined for it. To define a default string, first go to the ResourceView window (by clicking the ResourceView tab) and open the String Table resource into the string-table editor. Open the string-table editor by double-clicking on the String Table resource.

Now, double-click the blank line in the string-table editor, which brings up the String Properties dialog box. Type the new pane's command ID into the ID box and the default string into the Caption box (see Figure 11.20). (Instead of typing the command ID, you can choose it from the drop-down list.)


FIG. 11.20

Use the String Properties dialog box to define the new pane's default string.

Adding the ID to the Indicators Array

When MFC constructs your status bar, it uses an array of IDs to determine which panes to display and where to display them. As you now know, this array of IDs is passed as an argument to the status bar's SetIndicators() member function, which is called in the CMainFrame class's OnCreate() function.

To add your new pane to the array, type the pane's ID into the array at the position in which you want the new pane to appear in the status bar, followed by a comma. (The first pane, ID_SEPARATOR, should always remain in the first position.) Listing 11.17 shows the indicator array with the new pane added.

Listing 11.17 lst11_17.cpp Adding the New Pane's ID to the Indicator Array

static UINT indicators[] =
{
    ID_SEPARATOR,
    ID_CUSTOM_PANE,
    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
};

Creating the Pane's Command Update Handler

MFC does not automatically enable new panes when it creates the status bar. Instead, you must create a command update handler for the new pane, and enable the pane yourself. You must also add whatever code is needed to display information in the pane, assuming that the default string you defined in an earlier step is only a placeholder.

See “Writing Update-Command-UI Functions,” for more information on command-update handlers, (ch. 6)

First, you must declare the new command update handler in the mainfrm.h header file. The prototype you need to add to the header file's message map functions looks like this:

afx_msg void OnCustomPane(CCmdUI *pCmdUI);

Of course, the actual name of the handler will vary from pane to pane, but the rest of the line should look exactly as it does here.

Next, you have to add the handler to the class's message map (in mainfrm.cpp), which is what associates the command ID with the handler:

ON_UPDATE_COMMAND_UI(ID_CUSTOM_PANE, OnCustomPane)

As you learned in chapter 6, "Using Menus," you use the ON_UPDATE_COMMAND_UI macro to associate command IDs with their UI handers. The macro's two arguments are the command ID and the name of the command update handler.

Now, you're ready to write the new command update handler. In the handler, you have to enable the new pane, as well as set the pane's contents. Listing 11.18 shows the command update handler for the new pane:

Listing 11.18 lst11_18.cpp The Custom Pane's Update Handler

void CMainFrame::OnCustomPane(CCmdUI *pCmdUI) 
{
    pCmdUI->Enable(); 
    pCmdUI->SetText("This is a test");
}

If you don't understand how a command update handler works, please refer to chapter 6, "Using Menus," where these important functions that control a command item's appearance are discussed in detail. (No, a status bar pane is not a command item, but it uses the same mechanism for updating.)

Modifying a Panel's Appearance

The panels that comprise a status bar have style settings just like most other Windows graphical elements. You can manipulate the appearance of a panel by using the status bar object's member functions. The most useful is the SetPanelInfo() member function, which enables you to set the panel's ID, style, and width. A call to SetPanelInfo() looks like this:

statusBar.SetPaneInfo(index, id, style, width);

The index argument is the zero-based index of the pane whose info you want to set. (The first pane has an index of 0, the second an index of 1, and so on.). The id argument is the panel's ID, whereas style is the style flag for the panel, and width is the panel's width. There are several styles you can use with a status bar panel, which are shown in Table 11.3.

Table 12.3 Status Bar Panel Styles

Style Description
SBPS_DISABLED The panel does not display its contents.
SBPS_NOBORDERS The panel has no 3D border.
SBPS_NORMAL The panel has the default style.
SBPS_POPOUT The panel appears to pop out from the background rather than being recessed.
SBPS_STRETCH The pane stretches to fill unused space.

Creating the Final Version of the Toolbar Application

If you want to see the status bar tricks described in the previous sections in action, perform the following steps to modify the Toolbar application.

ON THE WEB

http://www.quecorp.com/semfc The complete source code and executable file for the Toolbar5 application can be found in the Chap11\Toolbar5 directory at this book’s Web site.

1. Choose the View, Resource Symbols command on Developer Studio's menu bar. When you do, you see the Resource Symbols dialog box.

2. Click the New button, and the New Symbol dialog box appears.

3. Type the ID_CUSTOM_PANE into the Name box, and click OK. Click Close to finalize your changes.

4. Click the ResourceView tab to go the ResourceView window, and open the String Table resource into the string table editor.

5. Double-click the blank line in the string-table editor. The String Properties dialog box appears.

6. Type ID_CUSTOM_PANE into the ID box and Default String into the Caption box. Press Enter to finalize your changes.

7. Load mainfrm.h, and add the following line to the message map function declarations, right after the line afx_msg void OnUpdateBlueUI(CCmdUI* pCmdUI), which you placed there previously:

afx_msg void OnCustomPane(CCmdUI *pCmdUI);

8. Load mainfrm.cpp, and add the following line to the class's message map, right after the line ON_UPDATE_COMMAND_UI(ID_BLUE, OnUpdateBlueUI), which you placed there previously:

ON_UPDATE_COMMAND_UI(ID_CUSTOM_PANE, OnCustomPane)

9. Replace the class's indicator-ID array with the array shown in Listing 11.19.

Listing 11.19 lst11_19.cpp The New Indicator Array

static UINT indicators[] =
{
    ID_SEPARATOR,
    ID_CUSTOM_PANE,
    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
};

10. In the OnCreate() function, change the 4 in the line m_statusBar.SetIndicators(indicators, 4) to a 5.

11. Add the following line near the end of OnCreate(), right before the return statement:

m_statusBar.SetPaneInfo(1, ID_CUSTOM_PANE, SBPS_POPOUT, [ic:ccc]30);

12. Add the function shown in Listing 11.20 to the end of the mainfrm.cpp file:

Listing 11.20 lst11_20.cpp The New Pane's UI Handler

void CMainFrame::OnCustomPane(CCmdUI *pCmdUI)
{
    pCmdUI->Enable(); 
    pCmdUI->SetText(m_displayStr);
}

You've now completed the final version of the Toolbar application. Choose Developer Studio's Build, Build command to compile and link the program. Then, choose Build, Execute (or just press Ctrl+F5) to run the application. When you do, you see the window shown in Figure 11.21. The window's status bar now displays a custom pane, which shows the currently selected combo box string. To change the contents of the custom pane, choose a new string from the toolbar's combo box.


FIG. 11.21

The Toolbar application's status bar now has a custom pane.

You now know everything you need to know to create almost any type of toolbar or status bar. Still, feel free to experiment with different style flags and settings when you create your toolbars and status bars. Although you should try to keep your window adornments looking as they do in most Windows applications, keeping them familiar to the user, you might come up with something useful for special situations. In the next chapter, “Property Sheets and Wizards,” you learn about property sheets, which are another important element of the modern Windows application.