The Lowly Status Bar

By: Kenn Scribner for TechTalk


Run the demo (all demos require Visual C++ 5.0!)
Download the demo source

Background: The first few lines of the article are really true...I did want to add a status bar to my applications. :) I was working in the C/SDK arena then, so I read Nancy Clut's book on programming the Windows 95 interface, which helped me get a status bar going with my old apps. But how to do it with C++/MFC? I found some obscure references, while other books mentioned it could be done and maybe gave a single line of code to do it (without all of the necessary steps to set up that code!). After bloodying my forehead a few times, I got it down. It's really quite simple, but someone once told me everything is simple once you know how.

The Lowly Status Bar

I remember my thoughts when I first saw an application with a status bar—you know, the informational control at the bottom of most application’s windows. I was wondering "how in the world did they do that?" Now, however, I use them myself in my own application programs. After all, Visual C++ generates them for us by default when we build a basic framework application using AppWizard. Some people, though, may not know how to tap their power to do cool and exotic things. In this issue, we’ll discuss some of the basics, then perform our first exotic trick, that being attaching a child window to the status bar (you’ll see why shortly). In a future issue, I’d like to discuss transparent bitmaps and how we might use them as other status bar "exotic-touch" fodder.

But first, the basics. When you build an initial application using Visual C++’s AppWizard, you have the option of incorporating and using a status bar. Assuming you’ve build such an application, let’s see what Visual C++ provided to us:

If all you ever want to do is work with the generic status bar, then you need do nothing. Just compile and go. However, if you want to be a bit more expressive, then you need to modify the default appearance and/or handling of the status bar. Most modifications are made to your source code such that the status bar is created slightly differently. We’ll do that here in this issue. However, you are also free to modify the size and style of the status bar, as well as add or remove panes, while the program is running, which I’m not going to show you here (perhaps in another issue if there is interest).

Enough talk, let’s make a change. Open this month’s demo MainFrm.cpp file and look for this code:

static UINT indicators[] =
{
	ID_SEPARATOR, // status line indicator
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
	ID_INDICATOR_OK
};

This code establishes an integer array of indicator values that are used by the status bar to determine how many panes to create and how large each pane should be. Visual C++’s default code looks very much like this, with the exception of the ID_INDICATOR_OK indicator. I added this indicator to this array (the text for which I placed in my string table), which will result in an additional pane when the status bar is created. Similarly, if you didn’t want to show the status of the <numlock> key, or any other key, simply remove the ID_INDICATOR_NUM (or corresponding) indicator. That pane will not be created. When we execute CMainFrame::OnCreate(), the following code will initialize the status bar and prepare it to be shown to the user:

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
} 

Now we have a status bar attached to our main window.

If we wanted to modify the text shown in any of the panes, we would execute this code somewhere in CMainFrame:

CString strMsg;
strMsg.LoadString(ID_INDICATOR_OK); 	// or any other string table text
				// resource or string
m_wndStatusBar.SetPaneText(4,strMsg,TRUE); // pane ‘4’ is our "OK" pane

Well, setting text is nice, but pretty boring. In my example, I might check for some sort of error condition somewhere, and if I found none, I’d display "OK". If I found an error, I might display "ERROR" or some other descriptive message. Ho hum. Yawn. Let’s do something a bit more fun!

You’ve probably noticed Visual C++ shows you the status of your project as its loading from disk by using a progress control, which has been installed onto Visual C++’s status bar. Mine looks like this:

What they’ve done is to create a new progress bar control and install it as a child window of the status bar. Then, they make the usual calls to the progress bar (perhaps using CProgressCtrl), which updates its visual display. Let’s do something similar, only we’ll use an animated control (which we learned about last issue). This animated control will "pop-up" when an error condition is noted. Okay, it’s a demo, so we won’t really see any error. It’ll show up when we press a toolbar button. We’ll use our imagination to believe there was some error or alert condition that the user must address. Don’t get too hung up over the type of control—any control could be used, whether it’s a button, progress bar, or some other home-brew control you’ve built.

First, we need to decide where to place our control. For the demo, I added a new pane (ID_INDICATOR_OK), so I’ll put the control there. We need to determine the window coordinates we require to create our control, so we’ll use the CStatusBar::GetItemRect() function to provide us with the coordinates we need. When we find these coordinates, we need to remember they delineate the "bounding rectangle", which encloses the entire pane. If we want to keep our 3D effect intact, we need to reduce the size of the rectangle before we pass it to our control’s creation routine. Here is the code I used in the demo:

CRect rcPane;
m_wndStatusBar.GetItemRect(4,&rcPane); // rect includes borders...
rcPane.DeflateRect(1,1); // so decrease size...

If you’ve examined the demo code, you noticed I called CMainFrame::RecalcLayout() before I asked for the pane’s bounding rectangle. I had to make this function call because the window has not been shown, which means the status bar’s pane rectangles are all invalid and useless. By calling RecalcLayout(), their sizes are calculated and are now useful to us. It’s a handy trick.

Once we have a window rectangle properly initialized, we have what we need to create a new control. For our animation control, I used this code:

m_CAnimCtrl.Create(WS_CHILD|ACS_CENTER|ACS_TRANSPARENT,rcPane,
			&m_wndStatusBar,IDR_ALERTAVI);
m_CAnimCtrl.Open(IDR_ALERTAVI);

Here, I created the control (note it is a child window) and loaded our AVI resource. Unlike a control in a dialog box, we must set the style bits for the control window by hand and call the creation routine (this is not handled for us, as it was last issue). In this case, I wanted transparent bitmaps used. I also asked it to be "centered"—this is important! This causes the control to draw the AVI bitmaps in the center of the window rectangle rather than their actual size. In this manner, I keep the size of the status bar unchanged, at the cost of a smaller animated image. We also must remember to later add code to CMainFrame::OnSize() to handle the case where the user is sizing the window (we’ll need to move this control in such a case).

For the demo, I added a menu item and toolbar button to activate the control. In my handler, I have all of the code I need to start the control’s animation and show it to the user. Conversely, this routine turns the control off and hides the window. It’s important to "turn off" the animation, as this control uses a separate thread to perform the animation. If you don’t need the animation to be in effect, you save CPU cycles by stopping the animation, which then terminates the thread (watch the threads shut down in Visual C++’s debugger window!). Here is my handler code:

void CMainFrame::OnViewAlert() 
{
	// Toggle state of alert indicator.
	m_bAlertActive = !m_bAlertActive;

	// Now, take action depending upon new state.
	if ( m_bAlertActive ) {
		// Blank the text. Note we don't repaint, as the control
		// will force a repaint when we show the child window in a
		// moment.
		m_wndStatusBar.SetPaneText(4,"",FALSE);

		// User may have resized window, so we determine where the
		// pane currently is and move the control window there.
		CRect rcPane;
		m_wndStatusBar.GetItemRect(4,&rcPane); // rect includes 
															// borders...
		rcPane.DeflateRect(1,1); // so decrease size...
		m_CAnimCtrl.MoveWindow(&rcPane,FALSE); // no need to repaint 
															// here

		// Start the control, then show the window.
		m_CAnimCtrl.Play(0,-1,-1);
		m_CAnimCtrl.ShowWindow(SW_SHOWNORMAL);
	} // if
	else {
		// Hide the window, then shut the control down.
		m_CAnimCtrl.ShowWindow(SW_HIDE);
		m_CAnimCtrl.Stop();

		// Fill in the text.
		CString strMsg;
		strMsg.LoadString(ID_INDICATOR_OK);
		m_wndStatusBar.SetPaneText(4,strMsg,TRUE);
	} // else
}

For this control, or any other control using additional memory, be sure to release the memory and/or resource when you’re finished. In my case, I added the CAnimateCtrl::Close() operation to my CMainFrame::DestroyWindow() function. This is more than just being nice…memory is a limited commodity and should be used properly. This is even more critical when you use the very limited "GDI Resource" memory, which an AVI file would use. When that gets filled, Windows could crash.

Well, that’s it! I’ve probably beat animation controls to death, but we did examine some neat effects we can provide our users via the status bar. As usual, you may download the demo program (see top of page). Also as usual, I used Visual C++ 5.0, so if you’re using an earlier version, you’ll need to rebuild the project if you would like to experiment and recompile. In another page page, we’ll take a look at "transparent bitmaps" and see how we might add other interesting effects to our status bar (or other window). Until then, happy coding!

Comments? Questions? Find a bug? Please send me a note!


[Back] [Left Arrow] [Right Arrow][Home]