The EX14B Status Bar Example

The EX14B example replaces the standard application framework status bar with a new status bar that has the following text panes.
Pane IndexString IDTypeDescription
0ID_SEPARATOR (0)Message line x cursor coordinate
1ID_SEPARATOR (0)Message line y cursor coordinate
2ID_INDICATOR_LEFTStatus indicatorLeft mouse button status
3ID_INDICATOR_RIGHTStatus indicatorRight mouse button status

The resulting status bar is shown in Figure 14-4. Notice that the leftmost pane stretches past its normal 1/20-screen length as the displayed frame window expands.

Click to view at full size.

Figure 14-4. The status bar of the EX14B example.

Follow these steps to produce the EX14B example:

  1. Run AppWizard to generate \vcpp32\ex14b\ex14b. Accept all default settings but two: select Single Document and deselect Printing and Print Preview. The options and the default class names are shown here.

  2. Use the string editor to edit the application's string table resource. The application has a single string table resource with artificial "segment" divisions left over from the 16-bit era. Double-click on the String Table icon in the String Table folder on the ResourceView page to bring up the string editor. Then double-click on the empty entry at the end of the list. A dialog allows you to assign the ID and the string value as shown below.

    Click to view at full size.

    Add two strings as follows.

    String IDString Caption
    ID_INDICATOR_LEFTLEFT
    ID_INDICATOR_RIGHTRIGHT

  3. Use Visual C++ to edit the application's symbols. Choose Resource Symbols from the View menu. Add the new status bar identifier, ID_MY_STATUS_BAR, and accept the default value.

  4. Use ClassWizard to add View menu command handlers in the class CMainFrame. Add the following command message handlers.

    Object IDMessageMember Function
    ID_VIEW_STATUS_BARCOMMANDOnViewStatusBar
    ID_VIEW_STATUS_BARUPDATE_COMMAND_UIOnUpdateViewStatusBar

  5. Add the following function prototypes to MainFrm.h. You must add these CMainFrame message handler prototypes manually because ClassWizard doesn't recognize the associated command message IDs.

    afx_msg void OnUpdateLeft(CCmdUI* pCmdUI);
    afx_msg void OnUpdateRight(CCmdUI* pCmdUI);
    

    Add the message handler statements inside the AFX_MSG brackets so that ClassWizard will let you access and edit the code later. While MainFrm.h is open, make m_wndStatusBar public rather than protected.

  6. Edit the MainFrm.cpp file. Replace the original indicators array with the following boldface code:

    static UINT indicators[] =
    {
        ID_SEPARATOR,  // first message line pane
        ID_SEPARATOR,  // second message line pane
        ID_INDICATOR_LEFT,
        ID_INDICATOR_RIGHT,
    };
    

    Next edit the OnCreate member function. Replace the following statement

    if (!m_wndStatusBar.Create(this) ||
        !m_wndStatusBar.SetIndicators(indicators,
          sizeof(indicators)/sizeof(UINT)))
    {
        TRACE0("Failed to create status bar\n");
        return -1;      // fail to create
    }
    

    with the statement shown here:

    if (!m_wndStatusBar.Create(this,
            WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, ID_MY_STATUS_BAR) ||
        !m_wndStatusBar.SetIndicators(indicators,
          sizeof(indicators)/sizeof(UINT)))
    {
        TRACE0("Failed to create status bar\n");
        return -1;      // fail to create
    }
    

    The modified call to Create uses our own status bar ID, ID_MY_STATUS_BAR, instead of AFX_IDW_STATUS_BAR (the application framework's status bar object).

    Now add the following message map entries for the class CMainFrame. ClassWizard can't add these for you because it doesn't recognize the string table IDs as object IDs.

    ON_UPDATE_COMMAND_UI(ID_INDICATOR_LEFT, OnUpdateLeft)
    ON_UPDATE_COMMAND_UI(ID_INDICATOR_RIGHT, OnUpdateRight)

    Next add the following CMainFrame member functions that update the two status indicators:

    void CMainFrame::OnUpdateLeft(CCmdUI* pCmdUI)
    {
        pCmdUI->Enable(::GetKeyState(VK_LBUTTON) < 0);
    }
    
    void CMainFrame::OnUpdateRight(CCmdUI* pCmdUI)
    {
        pCmdUI->Enable(::GetKeyState(VK_RBUTTON) < 0);
    }
    

    Note that the left and right mouse buttons have virtual key codes like keys on the keyboard have. You don't have to depend on mouse-click messages to determine the button status.

    Finally, edit the following View menu functions that ClassWizard originally generated in MainFrm.cpp:

    void CMainFrame::OnViewStatusBar()
    {
        m_wndStatusBar.ShowWindow((m_wndStatusBar.GetStyle() &
                                   WS_VISIBLE) == 0);
        RecalcLayout();
    }
    void CMainFrame::OnUpdateViewStatusBar(CCmdUI* pCmdUI)
    {
        pCmdUI-
    >SetCheck((m_wndStatusBar.GetStyle() & WS_VISIBLE) != 0);
    }
    

    These functions ensure that the View menu Status Bar command is properly linked to the new status bar.

  7. Edit the OnDraw function in Ex14bView.cpp. The OnDraw function displays a message in the view window. Add the following boldface code:

    void CEx14bView::OnDraw(CDC* pDC)
    {
        pDC->TextOut(0, 0,
            "Watch the status bar while you move and click the mouse.");
    }
    

  8. Add a WM_MOUSEMOVE handler in the CEx14bView class. Use ClassWizard to map the message to OnMouseMove, and then edit the function as shown below. This function gets a pointer to the status bar object and then calls the SetPaneText function to update the first and second message line panes.

    void CEx14bView::OnMouseMove(UINT nFlags, CPoint point)
    {
        CString str;
        CMainFrame* pFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;
        CStatusBar* pStatus = &pFrame->m_wndStatusBar;
        if (pStatus) {
            str.Format("x = %d", point.x);
            pStatus->SetPaneText(0, str);
            str.Format("y = %d", point.y);
            pStatus->SetPaneText(1, str);
        }
    }

    Finally, add the statement

    #include "MainFrm.h"

    near the top of the file ex14bView.cpp.

  9. Build and test the EX14B application. Move the mouse, and observe that the left two status bar panes accurately reflect the mouse cursor's position. Try the left and right mouse buttons. Can you toggle the status bar on and off from the View menu?

If you want the first (index 0) status bar pane to have a beveled border like the other panes and you want the status bar to grow and resize to fit their contents, include the following two lines in the CMainFrame::OnCreate function, following the call to the status bar Create function.

m_wndStatusBar.SetPaneInfo(0, 0, 0, 50);
m_wndStatusBar.SetPaneInfo(1, 0, SBPS_STRETCH, 50);

These statements change the width of the first two panes (from their default of one-fourth the display size) and make the second pane (index 1) the stretchy one.