|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIn an MFC application, the status bar text for menu items is a built-in feature. In the menu resource editor, you specify the text and, at runtime, as you move from menu item to menu item, a description will appear in the status bar for each one. By default, this functionality is missing from .NET Windows Forms applications. However, by using a component that implements the Status Bar Text Provider FeaturesThe
The supplied demo contains the assembly, a help file, and a demo application. Versions are supplied for .NET 1.1 and .NET 2.0. See the help file for details on installing the assembly in the Visual Studio .NET tool box. You can also extract the StatusBarTextProvider.cs source file for use in your own projects or control libraries. Using the Assembly in your ProjectsThe classes can be found in the assembly EWSoftware.StatusBarText.dll. In order to use the classes, add a reference to it in your project. The help file contains details on how to do this, if you need it. In the code modules that use classes from the Naturally, you must add a status bar control (.NET 1.1), or a status strip control containing at least one status label (.NET 2.0), to your form. .NET 1.1 SetupTo define the common application status bar used by all instances of // Define the default status bar to use in
// the main form's constructor
public MainForm()
{
InitializeComponent();
// Tell the StatusBarTextProvider component the status bar to use
StatusBarTextProvider.ApplicationStatusBar = sbStatusBar;
// You can also use any panel you like. The default is zero
// (the left-most panel). For the demo, we'll use the one
// in the middle.
StatusBarTextProvider.ApplicationDisplayPanel = 1;
}
If your status bar control does not contain panels, you can omit setting the .NET 2.0 SetupAlthough not formally deprecated, the standard When using a status strip, you specify the status label tool strip item used by all instances of // Define the default status strip label to use in
// the main form's constructor.
public MainForm()
{
InitializeComponent();
// Tell the StatusBarTextProvider component the component
// to use to display the text. When using a tool strip
// component, the ApplicationDisplayPanel property is ignored.
StatusBarTextProvider.ApplicationStatusBar = tslStatusText;
// Define the status label and progress bar too. This allows
// easy access to those items from anywhere within the
// application.
StatusBarTextProvider.StatusLabel = tslProgressNote;
StatusBarTextProvider.ProgressBar = tspbProgressBar;
}
Since a tool strip item is used in this situation, the The Instance Status BarOptionally, you can use the private void chkUseDialog_CheckedChanged(object sender, System.EventArgs e)
{
if(chkUseDialog.Checked)
{
// Use the dialog box's status bar when checked
sbMessage.InstanceStatusBar = sbDialog;
ucDemo.StatusBarTextProvider.InstanceStatusBar = sbDialog;
// Make the instance default text the same for any nested
// status bar text providers. If not, they pick up the
// status bar text of the current control as the default.
ucDemo.StatusBarTextProvider.InstanceDefaultText =
sbMessage.InstanceDefaultText;
}
else
{
// Go back to using the main form's status bar
sbMessage.InstanceStatusBar = null;
ucDemo.StatusBarTextProvider.InstanceStatusBar = null;
}
}
Design-time SupportTo use the Normally, you will just enter a message to display in the Status Bar Text on TabControl and TabPage Controls
One final note regarding the designer: When setting the status bar text for the tab pages, be sure to actually click on a part of the tab page to ensure that it is selected. If you just click the tab in the tab control's header to select it, the tab control will have the focus in the designer, and you may inadvertently modify the tab control's status bar text rather than the text for the tab page. Status Text on Tool Strip ItemsUnder .NET 2.0, status bar text can be specified for menu strip, tool strip, and status strip items. However, there are a few limitations with no workarounds.
How it WorksTo create an extender provider, you need to derive a class from Throughout the following code, the public class StatusBarTextProvider : Component, IExtenderProvider
{
/// <summary>
/// Default constructor
/// </summary>
public StatusBarTextProvider()
{
htOptions = new Hashtable(25);
}
/// <summary>
/// Constructor. This one takes a reference to a container.
/// </summary>
/// <param name="container">The container for the
/// component</param>
public StatusBarTextProvider(IContainer container) : this()
{
if(container != null)
container.Add(this);
}
/// <summary>
/// This is implemented to determine if the component can be
/// extended with the extra properties.
/// </summary>
/// <param name="extendee">The object to check</param>
public bool CanExtend(object extendee)
{
// MenuItem is a Component. LinkLabel derives from Label but
// it can gain the focus and thus can be extended. For .NET 2.0,
// we also support the ToolStripItem component.
#if !DOTNET_20
if(extendee is MenuItem || extendee is LinkLabel)
#else
if(extendee is MenuItem || extendee is LinkLabel ||
extendee is ToolStripItem)
#endif
return true;
// Non-Control types, Form, and these specific controls can't be
// extended as it doesn't make sense for them as they don't gain
// the focus needed to display the text.
if(!(extendee is Control) || extendee is Form ||
extendee is Label || extendee is PictureBox ||
extendee is ProgressBar || extendee is ScrollBar ||
extendee is Splitter || extendee is StatusBar ||
#if !DOTNET_20
extendee is ToolBar)
#else
extendee is ToolBar || extendee is ToolStrip)
#endif
return false;
// All other Control types can be extended
return true;
}
}
In addition, you need to specify the [ProvideProperty("StatusBarText", typeof(Component)),
ProvideProperty("ShowAsBlank", typeof(Component))]
public class StatusBarTextProvider : Component, IExtenderProvider
{
... class code ...
}
The next step is to actually implement the code for the provided properties. Although they act like properties in the designer, you actually create two methods (a /// <summary>
/// This class contains the options for the items that need status
/// bar text.
/// </summary>
private sealed class PropertyOptions
{
//============================================================
// Private data member
private string message;
private bool showAsBlank;
//============================================================
// Properties
/// <summary>
/// Set or get the message text
/// </summary>
public string Message
{
get { return message; }
set { message = value; }
}
/// <summary>
/// The "show as blank" flag
/// </summary>
public bool ShowAsBlank
{
get { return showAsBlank; }
set { showAsBlank = value; }
}
//============================================================
// Methods, etc.
/// <summary>
/// Constructor
/// </summary>
/// <param name="msg">The message text to display</param>
/// <overloads>There are two overloads for the
/// constructor</overloads>
public PropertyOptions(string msg)
{
message = msg;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="showBlank">The "show as blank"
/// flag</param>
public PropertyOptions(bool showBlank)
{
showAsBlank = showBlank;
}
}
Here is an example of a Get method for the /// <summary>
/// This is used to retrieve the status bar text for a component.
/// </summary>
/// <param name="comp">The component for which to get the
/// status bar text</param>
/// <returns>The message string if found or null if not
/// found</returns>
[Category("StatusBar"), Localizable(true), DefaultValue(null),
Description("The status bar text for the item")]
public string GetStatusBarText(Component comp)
{
if(comp == null)
throw new ArgumentException("Component cannot be null");
#if !DOTNET_20
if(!(comp is MenuItem) && !(comp is Control))
#else
if(!(comp is MenuItem) && !(comp is Control) &&
!(comp is ToolStripItem))
#endif
throw new ArgumentException(
"Component must be a MenuItem, ToolStripItem, " +
"or a Control");
if(htOptions.Contains(comp))
return ((PropertyOptions)htOptions[comp]).Message;
return null;
}
The Set method works in a similar fashion. If the hash table does not contain the component, you create an instance of the property options class, store the value in it, and add it to the hash table. If the component is already in the hash table, you simply retrieve the existing settings and update them with the new value. In the case of /// <summary>
/// This stores the status bar text for the specified component.
/// </summary>
/// <param name="comp">The component associated with the
/// message</param>
/// <param name="message">The status bar text for the
/// component</param>
public void SetStatusBarText(Component comp, string message)
{
if(comp == null)
throw new ArgumentException("Component cannot be null");
MenuItem mi = comp as MenuItem;
Control ctl = comp as Control;
TabControl tc = comp as TabControl;
#if DOTNET_20
ToolStripItem ti = comp as ToolStripItem;
ToolStripControlHost tsch = comp as ToolStripControlHost;
if(mi == null && ti == null && ctl == null)
#else
if(mi == null && ctl == null)
#endif
throw new ArgumentException(
"Component must be a MenuItem, " +
"ToolStripItem, or a Control");
if(message != null && message.Length == 0)
message = null;
If the hash table does not already contain the object, we create a new property object containing the message, and add it to the hash table using the object as the key. Then, based on the object type, we hook up one or more event handlers that will display the appropriate status bar text. This step is skipped at design-time though. For If it is a tab control, we also have to hook up the if(!htOptions.Contains(comp))
{
htOptions.Add(comp, new PropertyOptions(message));
if(!this.DesignMode && message != null)
if(mi != null)
mi.Select += new EventHandler(Menu_Select);
else
#if DOTNET_20
if(ti != null)
{
ti.MouseEnter += new EventHandler(Control_Enter);
ti.MouseLeave += new EventHandler(Control_Leave);
// If it's a control host, hook the enter and
// leave events too.
if(tsch != null)
{
tsch.Enter += new EventHandler(Control_Enter);
tsch.Leave += new EventHandler(Control_Leave);
}
}
else
#endif
{
ctl.GotFocus += new EventHandler(Control_Enter);
ctl.Enter += new EventHandler(Control_Enter);
ctl.Leave += new EventHandler(Control_Leave);
// See article notes above
if(tc != null)
tc.SelectedIndexChanged += new EventHandler(
Control_Enter);
}
}
If the object already exists in the hash table, we need to update the value in the existing property settings. In addition, if the property is cleared (i.e., set to a else
{
PropertyOptions po = (PropertyOptions)htOptions[comp];
po.Message = message;
if(!this.DesignMode && message == null &&
po.ShowAsBlank == false)
if(mi != null)
mi.Select -= new EventHandler(Menu_Select);
else
#if DOTNET_20
if(ti != null)
{
ti.MouseEnter -= new EventHandler(Control_Enter);
ti.MouseLeave -= new EventHandler(Control_Leave);
// If it's a control host, unhook the enter and
// leave events too.
if(tsch != null)
{
tsch.Enter -= new EventHandler(Control_Enter);
tsch.Leave -= new EventHandler(Control_Leave);
}
}
else
#endif
{
ctl.GotFocus -= new EventHandler(Control_Enter);
ctl.Enter -= new EventHandler(Control_Enter);
ctl.Leave -= new EventHandler(Control_Leave);
if(tc != null)
tc.SelectedIndexChanged -= new EventHandler(
Control_Enter);
}
}
The Menu Item Event HandlersThe event handlers are where most of the action takes place. For menu items, the // This is handled to display status bar text for a MenuItem
// component.
private void Menu_Select(object sender, EventArgs e)
{
if(this.StatusBar == null || !htOptions.Contains(sender))
return;
// Hook the MenuComplete event on first use to restore
// the existing status bar text.
if(!hookedMenuEvents)
{
Form frm = this.StatusBarParentForm;
if(frm != null)
{
frm.MenuComplete += new EventHandler(
Form_MenuComplete);
hookedMenuEvents = true;
}
}
this.CurrentStatusBarText = this.ItemText(sender);
}
// This is handled so that the old status bar text is restored
// when the component status bar text is no longer needed.
private void Form_MenuComplete(object sender, System.EventArgs e)
{
if(this.StatusBar == null)
return;
this.CurrentStatusBarText = this.StatusBarDefaultText;
}
Other Control Event HandlersFor all other controls, the // This is handled to display status bar text when a control
// is entered or gains the focus.
private void Control_Enter(object sender, EventArgs e)
{
if(this.StatusBar == null || !htOptions.Contains(sender))
return;
// Hook the Closed event to reset the status bar text
// when the form is closed. When it's modal, it doesn't
// always get the Leave event to reset the text. The
// Activated and Deactivated events are also hooked to
// set and restore the text.
if(!hookedFormEvent && this.StatusBar != null)
{
Control p = sender as Control;
if(p != null)
p = p.Parent;
#if DOTNET_20
else // It's a control hosted in a tool strip item
p = ((ToolStripItem)sender).Owner.Parent;
#endif
while(p != null)
{
Form frm = p as Form;
if(frm != null)
{
frm.Activated += new EventHandler(
Form_Activated);
// Same handler as Form.MenuComplete
frm.Deactivate += new EventHandler(
Form_MenuComplete);
frm.Closed += new EventHandler(
Form_MenuComplete);
hookedFormEvent = true;
break;
}
p = p.Parent;
}
}
this.CurrentStatusBarText = this.ItemText(sender);
}
// This is handled to display status bar text when a control
// is left.
private void Control_Leave(object sender, EventArgs e)
{
if(this.StatusBar == null || !htOptions.Contains(sender))
return;
this.CurrentStatusBarText = this.StatusBarDefaultText;
}
// This is handled to redisplay the status bar text for the
// form's focused control when activated.
private void Form_Activated(object sender, System.EventArgs e)
{
Form frm = sender as Form;
if(frm != null && this.StatusBar != null)
{
Control ctl = frm.ActiveControl;
// Nested controls may not have any so walk up
// the parent chain to see if they do.
while(ctl != null && !htOptions.Contains(ctl))
ctl = ctl.Parent;
if(ctl != null)
this.CurrentStatusBarText = this.ItemText(ctl);
}
}
Demonstration ApplicationsA Windows Forms application in C# and VB.NET is provided that demonstrates the basic use of the status bar text provider. A version of each is provided for .NET 1.1 and .NET 2.0. The .NET 1.1 version demonstrates the use of the provider with menu items and with controls in modal and non-modal forms. The .NET 2.0 version demonstrates the use of the provider with menu strip, tool strip, and status strip items, along with standard controls in modal and non-modal forms. It also demonstrates the use of the extra For either version, use the File | Load option to open non-modal instances of the demo form. Use the Help | About option to open a modal instance of the demo form. The demo form allows you to switch between using the application's status bar/status strip and the demo form's status bar for displaying the messages. Revision History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||