Bitmaps and Other CStatusBar Customizations

Dale Rogerson
Microsoft Developer Network Technology Group

April 18, 1995

Click to open or copy the files in the EasyBit sample application for this technical article.

Click to open or copy the files in the GLlib DLL for this technical article.

Abstract

This article shows how to customize the status bar created by CStatusBar (which is a class in the Microsoft® Foundation Class [MFC] Library) in several ways, including changing the sizes of the panes in a status bar, changing the height of the status bar, using a different font, and displaying a bitmap in one of the panes. The EasyBit sample application demonstrates the techniques described in this article.

Please note that future versions of MFC may not support some customizations of CStatusBar.

Introduction

When I wrote the EasyBit sample application, I wanted to demonstrate how you could place three-dimensional (3-D) rendered objects anywhere in your application's window by rendering OpenGL™ commands on a bitmap. I decided that it would be exciting to put a 3-D rendered object on the status bar. This article explains how I customized the CStatusBar class in the Microsoft® Foundation Class (MFC) Library to display a bitmap on the status bar. In the customization process, I also:

For information on how I rendered the 3-D objects for the status bar, see my series of articles on OpenGL, especially "OpenGL VI: Rendering on DIBs with PFD_DRAW_TO_BITMAP," in the Microsoft Development Library.

Bitmap on the Status Bar

My OpenGL sample application, EasyBit, rotates one of three 3-D shapes. I wanted to show the user which shape was currently selected for rotation, so I decided to display the shape on the EasyBit status bar. I wanted the status bar to look like the one illustrated in Figure 1 below.

Figure 1. Status bar with bitmap

To perform this magic, I did some searching on the Microsoft Development Library and found the Knowledge Base article Q98864,"Displaying a Bitmap in a CStatusBar Pane." With this article, putting a bitmap onto the status bar became a trivial task.

Following the instructions in the Knowledge Base article, I created a new class derived from CStatusBar (not CStatusBarCtrl):

class CMyStatusBar : public CStatusBar
{
   .
   .
   .
   void DoPaint(CDC* pdc) ;
   .
   .
   .
};

Before I could draw a bitmap on the status bar, I needed to know how to draw on the status bar. I implemented the following DoPaint function, which simply draws the status bar and paints the second pane red:

void CMyRedStatusBar::DoPaint(CDC* pDC)
{
   // Draw the status bar.
   CStatusBar::DoPaint(pDC) ;

   // Get the bounding rectangle of the second pane.
   CRect aRect ;
   GetItemRect(1, &aRect) ;

   // Fill the pane with the color red.
   CBrush aBrush(RGB(255,0,0)) ;
   CBrush* pOldBrush = (CBrush*)pDC->SelectObject(&aBrush) ;
      pDC->Rectangle(&aRect) ;
   pDC->SelectObject(pOldBrush) ;
}

Now that I knew how to draw on the status bar, I could use my CSimpleDIB class to draw the bitmap. CSimpleDIB is discussed in "OpenGL VII: Scratching the Surface of Texture Mapping" in the Development Library.

void CMyStatusBar::DoPaint(CDC* pDC)
{
   // Draw the status bar.
   CStatusBar::DoPaint(pDC) ;

   // Return if no DIB to draw.
   if (m_iCurrentDIB == -1) return ;

   CRect aRect ;
   GetItemRect(1, &aRect) ;

   // Select palette.
   CPalette* pOldPal = NULL;
   CPalette* pPalTemp = m_pScenes[m_iCurrentDIB]->GetPalette() ;
   if (pPalTemp != NULL)
   {
      pOldPal = pDC->SelectPalette(pPalTemp, FALSE) ;
      pDC->RealizePalette() ;
   }

   // Draw using the CSimpleDIB class.
   m_DIB[m_iCurrentDIB].Draw(pDC, aRect.left, aRect.top) ;

   // Select old palette.
   if (pOldPal != NULL) pDC->SelectPalette(pOldPal, FALSE) ;
}

The next step is to replace:

CStatusBar m_wndStatusBar ;

in MAINFRM.H with:

CMyStatusBar m_wndStatusBar ;

As the final step, I added a second ID_SEPARATOR to the indicators array in MAINFRM.CPP. This adds another pane (on which I will draw the bitmap) to the status bar:

static UINT BASED_CODE indicators[] =
{
   ID_SEPARATOR,      // status line indicator
   ID_SEPARATOR,      // pane for drawing bitmap
   ID_INDICATOR_CAPS,
   ID_INDICATOR_NUM,
   ID_INDICATOR_SCRL,
};

Sizing the Status Bar

Well, after I put the bitmap on the status bar, I discovered that I couldn't see it—it was just too small. I also noticed that the indicator panes were missing. My status bar looked like the one shown in Figure 2 below.

Figure 2. Status bar with default font

I decided to increase the height of the status bar so I could see the bitmap. However, the method for doing this wasn't obvious, because MFC doesn't have any CStatusBar::SetHeight functions. After digging into the guts of the status bar in the MFC BARSTAT.CPP source file, I discovered that the height of the status bar is determined by its font. To change the height of the status bar, you must change the height of the font you use in the status bar.

Now, how do you change the font? Do you use a member function in CStatusBar? CControlBar? CWnd? As it turns out, you use CWnd::GetFont and CWnd::SetFont. Therefore, I did just that in the CMyStatusBar::Init function, which I called from CMainFrame::OnCreate:

   CFont* pFont = GetFont();
   LOGFONT logFont ;
   pFont->GetObject(sizeof(LOGFONT),(void*)&logFont ) ;
   logFont.lfHeight *= 2 ;

   m_Font.CreateFontIndirect(&logFont) ; 
   SetFont(&m_Font, TRUE) ;

This function uses CWnd::GetFont and CWnd::SetFont to get the font, double its height, and then set it to the new font. The results are shown in Figure 3.

Figure 3. Status bar with double-height default font

As you can see in Figure 3, the default typeface looks blocky when it is enlarged, so I changed it to Arial by adding the line shown in boldface below:

   CFont* pFont = GetFont();
   LOGFONT logFont ;
   pFont->GetObject(sizeof(LOGFONT),(void*)&logFont ) ;
   logFont.lfHeight *= 2 ;

   lstrcpy(logFont.lfFaceName, _T("Arial"));


   m_Font.CreateFontIndirect(&logFont) ; 
   SetFont(&m_Font, TRUE) ;

Changing this single line resulted in the smooth curves of a TrueType® font replacing the jagged edges of the previous font, as seen on TV and in Figure 4 below.

Figure 4. Status bar with double-height Arial TrueType font

If you want your application to support the double-byte character set (DBCS), you should use:

GetSystemMetrics(SM_DBCSENABLED) ;

to determine whether the system is using DBCS, and pick a DBCS-compatible font.

Now all we have to do is get the indicator panes back and resize our bitmap pane.

Sizing the Panes

The CStatusBar::SetPaneInfo function sets the size and style of each pane in a status bar. If you don't know the current size of a pane, it is difficult to calculate its final size. The CStatusBar::GetPaneInfo function gets the size and style of each pane. I call both of these functions from CMyStatusBar::Init. The code below does the resizing.

UINT nID[5], nStyle[5] ;
int iWidth[5] ;   
GetPaneInfo(0, nID[0], nStyle[0], iWidth[0]) ;
GetPaneInfo(1, nID[1], nStyle[1], iWidth[1]) ;
GetPaneInfo(2, nID[2], nStyle[2], iWidth[2]) ;
GetPaneInfo(3, nID[3], nStyle[3], iWidth[3]) ;
GetPaneInfo(4, nID[4], nStyle[4], iWidth[4]) ;
iWidth[1] = iWidth[0]/2 - (iWidth[2]+iWidth[3]+iWidth[4]);
SetPaneInfo(0, nID[0], SBPS_STRETCH, 0) ;
SetPaneInfo(1, nID[1], nStyle[1], iWidth[1]*2) ;
SetPaneInfo(2, nID[2], nStyle[2], iWidth[2]*2) ; // CAPS
SetPaneInfo(3, nID[3], nStyle[3], iWidth[3]*2) ; // NUM 
SetPaneInfo(4, nID[4], nStyle[4], iWidth[4]*2) ; // SRCL

GetParentFrame()->RecalcLayout() ;

I did not change any of the pane IDs. I didn't change pane styles, either, except for pane 0 (the status pane). The status pane normally includes the SBPS_NOBORDERS style, which I removed because I like the 3-D effect. The width of pane 0 is set to 0. Pane 0 has the SBPS_STRETCH style and will stretch to fill the space left over from the other panes.

I doubled the width of the indicator panes (panes 2, 3, and 4) to make room for the bigger font. The width of pane 1, which will contain our bitmap, is equivalent to the initial width of pane 0 minus the width of the indicator panes.

Figure 5 shows the status bar after I added the code to resize the panes.

Figure 5. Status bar with bitmap

Conclusion

When I decided to add a bitmap to the EasyBit status bar, I found that drawing on the status bar was not difficult. However, it was not intuitive that changing the font would resize the status bar. Once I figured this out, the customization itself became pretty straightforward.

A final word of caution: Make sure that you have a good reason for customizing the status bar through CStatusBar—remember that future versions of MFC may not support your customizations.