Whenever I create a major application--especially an MDI app--I often have commands (program actions) that can be invoked by the user in multiple places--i.e., from a menu and from a toolbar. That could mean 2 procedures in the MDI module that do the same thing--as well as the need to set similar properties for each
ToolStripItem (menu and toolbar item)--say, whenever a command needs to be enabled/disabled or shown/hidden. Also, the code executed for each command, while different overall, often contains certain common instructions--say, at the beginning and end of the procedure. Finally, I often want to detect when a
ToolStripItem is selected (highlighted) or deselected (unhighlighted)--say, for displaying text in a status bar label; unfortunately, menu items raise no specific events for when they are selected/deselected.
The following class library scans a form's control collection for
ToolStripItem containers for
ToolStripItems, and groups those items by command name (specified by
ToString method of
Tag property) into a "command dictionary" within instances of the class
MDICommandInfo--which allow one to specify status-bar text and get/set properties of all
ToolStripItems associated with a given command at once.
MDICommandInfo instances are managed by another class,
MDICommandHandler--which intercepts the mouse/keyboard events needed in order to detect when commands are invoked (clicked), selected, and deselected--and delegates those situations to 3 events:
CommandDeselected, respectively. The list of command names and associated status texts is supplied by the user using a
ResourceManager, using a
Dictionary, or manually.
- As of 11/28/2017, the code has been updated so as to avoid multiple firings of events. Each
AddHandler statement is preceded by a
RemoveHandler statement so that successive calls to
AddChildItems don't create redundant event handling.
- As of 2/5/2018, the main class no longer relies on a life-long access to a
ResourceManager, and therefore the
ResourceManager property no longer exists! The list of command names and associated status texts is specified by either a
ResourceManager or a
Dictionary(Of String, String)--through the constructor or through the respective
- As of 12/12/2018, the
IsCommandVisible properties are now Nullable, so that internal (and external) code that sets them to Nothing (some yes, some no) doesn't set them to
False (all no) by default. This is an error I only noticed very recently!
Using the Code
MDICommandHandler (main class),
MDICommandInfo (helper class)
MDICommandHandlerInfo Class (Helper Class)
String for type of program action (
Tag.ToString of corresponding
StatusLabelText = optional
String for status-bar text for this command
ToolStripItems corresponding to this command
CommandName gets name of command (specified in constructor);
StatusLabelText gets or sets status-bar text
IsCommandEnabled (Nullable Boolean) gets or sets the
Enabled property of all the
ToolStripItems (when getting,
Nothing indicates that some are enabled, some disabled)
IsCommandVisible (Nullable Boolean) gets or sets the
Visible property of all the items (once again,
Nothing indicates mixed information)
SetProperty sets an arbitrary property (specified by
String) of the items to a given
index() information in the event that the property has parameters--using reflection.
Contains checks to see if control
ToolStripItem is in the
Remove allow you to insert or delete a
ToolStripItem item, respectively.
MDICommandHandler Class (Main Class)
MDIParentForm is any WinForms
Form (does not have to be an MDI parent form)
StatusLabel is a
ToolStripStatusLabel used to display status bar text. If parameter is
Nothing, you'll have to assign text to a label or other control manually
StatusTextDictionary is an optional
ResourceManager object or
Dictionary(Of String, String) object, respectively, which contains a list command-name/status-text associations--using either resources (resource name specifies command name, with underscores where spaces are intended and "
_Tag" appended; resource
string specifies status text), or a dictionary (key specifies command name; value specifies status text)--for each command's status-bar text. If parameter is omitted, you'll have to manually generate the
Strings which will be assigned to the
StatusLabelText property and to
StatusLabel (or your own status control).
MDIParentForm gets the form (specified in the constructor)
StatusLabel gets or sets status bar control (Set to
Nothing to handle label control and/or description text manually.)
CommandInfo gets instance of
MDICommandInfo corresponding to action specified by
ToolStripItem). This is the default property.
CommandDictionary gets a
MDICommandInfo instances, representing all participating commands. The key is the command-name
String; the value is the status-text
AddItem adds a
ToolStripItem to the command dictionary; if the item is a
AddChildItems is called to handle the drop-down list.
AddChildItems adds all
ToolStripItems inside a
ToolStripItemCollection; this method is recursive, and is automatically invoked by the constructor for the entire form.
GetCommandsFromDictionary gets a list of command-name/status-text associations using either a
ResourceManager instance or a
Dictionary(Of String, String) instance, respectively. Resource-name/dictionary-key specifies command name, and resource-string/dictionary-value specifies status text for a command. The first parameter, either
StatusTextDictionary, is the resource-manager/dictionary instance, the optional second parameter,
Clear (defaults to
False), specifies whether to initially clear out command dictionary (
True) or to simply change status text when an item is found to be pre-existing in it (
- If you want an individual
ToolStrip item to be omitted from the dictionary, then leave its
- Any showing or hiding of status-bar text occurs before the
CommandDeselected event is fired.
- If the associations of command names and status texts are specified via a
ResourceManager, then the key-names of the resources must echo the command names (
Tag.ToString) with all spaces replaced with underscores ("
_") and with "
_Tag" appended--i.e., command "
This Command" must have a resource name
specified by key-string "
This_Command_Tag". This rule does not apply when specifying associations via a
Dictionary (that is, "
This Command" has a dictionary-key value "
- If the
StatusLabelText property of a command's
MDICommandInfo instance is set to a non-null
MDICommandHandler has been instantiated or the most recent call to
GetCommandsFromDictionary, then the
StatusLabelText value overrides any pre-defined text for that command.
Dim mch As MDICommandHandler = _
New MDICommandHandler(Me, StatusLabel, Resource3)
Dim mch As MDICommandHandler = _
New MDICommandHandler(Me, StatusLabel, Dictionary1)
mch.CommandInfo("Save").IsCommandEnabled = ShouldWeSave
Dim CanWeSave As Boolean? = mch.CommandInfo("Save").IsCommandEnabled
mch("New").StatusLabelText = "THIS COMMAND has manually set text"
mch.GetCommandsFromDictionary(Dictionary2, False) : mch.AddChildItems(Me)
AddHandler mch.CommandClicked, AddressOf mch_CommandClicked
Private Sub mch_CommandClicked(sender As Object, e As MDICommandHandlerEventArgs)
Select Case e.CommandName