Introduction
For anyone who is moving to C# and Windows Forms from MFC, you may be used to your AppWizard-generated applications automatically displaying a prompt string in the status bar when you move your mouse over the items in your program's menus. This sort of help has come to be expected by users in all major thick-client GUI applications.
This article shows how to implement this functionality in a Windows Forms app. The only controls you will need for your form are a MenuStrip
, some menus and menu items on it, and a StatusStrip
control with at least one ToolStripStatusLabel
control on it.
The key to implementing this functionality lies in the handling of the DropDownClosed
event for each top-level menu item (i.e. the File and Help menus themselves) and then the MouseMove
event for each menu item (such as New and About).
Background
In the newest versions of Visual Studio.NET, there is no AppWizard for C# projects -- at least not one as comprehensive as the AppWizard we've all come to know and love for use with MFC and C++.
So you can't get a fully-functional, MFC-style GUI application automatically generated for you when you select to create a new, C# Windows Forms project. What you do start out with is the following:
In the sample project included with this article, I renamed the default Form1
to MainForm
, and altered its Text
property as shown. Visual Studio doesn't do all the work for you like back in the good ol' days. You have to roll your own.
Most folks develop thick-client GUI applications with C# and add menus and a status bar, and put a text label on the status bar saying "Ready," or "Welcome," and then leave it at that. This looks unprofessional when you roll over menu items and no prompts display accordingly. Convention dictates that you should display a helpful prompt message for each menu item if you are using a status bar in your application. Adding such functionality is what this article illustrates.
Using the Code
The sample program included with this article is a 'mock' text editor program. The menu items -- save for the File->Exit menu item -- don't, in fact, work. That's because editing text or doing anything useful with this sample is not the point. The sample is just meant to illustrate how you handle menus and show prompts on the status bar in response to the user moving the mouse over the menus to find the command the user wants.
To begin, we start with the blank, default form shown above, and then we open the Toolbox and add to our new form MenuStrip
, TextBox
, and StatusStrip
controls. The MenuStrip
and StatusStrip
controls automatically dock to the top and bottom of the form, respectively. This is what we have come to expect from these controls.
We set the Multiline
property of the TextBox
to true
, and then we set its Dock
property to Fill
, so it fills the remaining client area of the form. Editing on the MenuStrip
, we add File and Help menus to the menu bar. We add New and Exit commands to the File menu, and we add an About command to the Help menu.
Click the File menu, and then double-click the Exit command to add a Click
event handler for it. Once the handler is in place, implement it as follows:
private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
this.Close();
}
Too easy. The handler for the Exit command simply closes the form. OK, now we're ready to implement our status bar prompts. First, go back to the Designer, and click the left mouse button on the StatusStrip
control. Click the placeholder to display a menu of controls to add, and then select ToolStripStatusLabel
. An example of this is shown below:
Once the new ToolStripStatusLabel
is in place, we want it to expand to take up all the available space in the StatusStrip
control. To do this, we set the control's Spring
property to true
. Right-click the toolStripStatusLabel1
control and then click Spring in the context menu which appears, as shown:
To follow GUI development conventions, I prefer for the ToolStripStatusLabel
's text to be aligned with the left side of the window. So I am going to set the TextAlign
property to LeftTop
using Visual Studio's Properties window. I also altered the control's Name
property to just say statusLabel
, for simplicity. Finally, I altered the Text
property of the control to say Ready, which is a nice default prompt. If your application supports context-sensitive Help, then you can go ahead and also put, For Help, press F1
.
What about the actual menu-item prompts themselves? If you will recall, back in the old MFC days, the prompt was stored in the application's Resource Script (.rc) file, using the same identifier as the menu item itself. The framework did the rest. Here, using Windows Forms, we have a little more leeway.
I went into Solution Explorer, expanded the Properties folder underneath the icon for the project, and then double-clicked on the Resources.resx file to open the resource editor. I selected Strings as the section I wanted to edit and then I put the following information in the table:
As you can see, we can give our prompt strings any sort of name (equivalent to the old Identifier in MFC) we like. We just have to make sure and use the right string name for the right menu item.
Now go back to the MainForm
Designer, and do the following:
- Click with the left mouse button on the word "File" on the menu bar. Open the Properties window, and then click the Events button.
- Scroll down and click once on the
DropDownClosed
event.
- In the right-hand column, type in
menuClosed
.
- Click on the Help menu and repeat steps 1-3.
- Make sure to rename your new
DropDownClosed
event handler to menuClosed
as before, so that both menus are handled by the same code.
The DropDownClosed
event is fired whenever a top-level menu closes after the user is finished with it. We want whatever text we've been displaying on the status bar for a menu item prompt to go away and be replaced with the default, "idle" message once the user has finished with a menu. To wit, we write the following code for the menuClosed
event handler:
private void menuClosed(object sender, EventArgs e) {
this.statusLabel.Text = Properties.Resources.idleMessage;
}
The statement Properties.Resources.idleMessage
accesses the idleMessage
string in the Resource.resx
file's string table as shown above.
Next, we go through the File and Help menus, and for each item in turn:
- Single-click the item, open the Properties window, and then click the Events button.
- Double-click the
MouseMove
event to add a handler for it in your code.
In each handler, all you do is set the Text
property of the statusLabel
control to the appropriate string from the Resources.resx file using the Properties.Resources
class.
For example, for the New, Exit, and About menu items in our sample, the MouseMove
event handlers are implemented thus:
private void newToolStripMenuItem_MouseMove(object sender, MouseMoveEventArgs e) {
this.statusLabel.Text = Properties.Resources.newFile;
}
private void exitToolStripMenuItem_MouseMove(object sender, MouseMoveEventArgs e) {
this.statusLabel.Text = Properties.Resources.exit;
}
private void aboutToolStripMenuItem_MouseMove(object sender, MouseMoveEventArgs e) {
this.statusLabel.Text = Properties.Resources.about;
}
And there we have it! We have completed implementing the sample. Hit the F5 key on your keyboard, and you will find that the sample behaves as illustrated in the figure at the very top of this article.
History
- 11th March, 2010: Article written