Click here to Skip to main content
14,550,734 members

Handling Menus and Toolbars Using MDICommandSupport Class Library

Rate this:
4.53 (7 votes)
Please Sign up or sign in to vote.
4.53 (7 votes)
2 Nov 2019CPOL
Simplifies working with ToolStrip-type menus and tool bars for MDI-parent and other forms; also works with ToolStrip-type context menus

This article has been updated November 3, 2019. The demo application now features submenus and a context menu. The main class now also includes automatic support for ContextMenuStrip context menus for any form/control that features such menus.

Image 1


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 (any form, not just MDI's) control collection for MenuStrips, ToolStrips, and 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.

The 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: CommandClicked, CommandSelected, and CommandDeselected, respectively. The list of command names and associated status texts is supplied by the user using a ResourceManager, using a Dictionary, or manually.

It can also be used on ContextMenuStrips.

History of Changes

From most recent to earliest:

  1. As of 11/3/2019, the clas now plugs in any ContextMenuStrip encountered automatically. Previously, one had to use AddChildItems to add support for context menus manually.
  2. As of 11/1/2019, the demo project features submenus and a context menu.
  3. As of 12/12/2018, the IsCommandEnabled and 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 by chance!
  4. 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 GetCommandsFromResources or GetCommandsFromDictionary methods.
  5. 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.

Using the Code

Root Namespace: MDICommandSupport

Classes: MDICommandHandler (main class), MDICommandInfo (helper class)

MDICommandHandlerInfo Class (Helper Class)

  • CommandName = String for type of program action (Tag.ToString of corresponding ToolStripItems)
  • StatusLabelText = optional String for status-bar text for this command
  • CommandItems gets List of 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 PropertyName String) of the items to a given NewValue--with optional index() information in the event that the property has parameters--using reflection.
  • Contains checks to see if control ToolStripItem is in the CommandItems List.
  • Add and 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
  • ResourceManager or 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 MDICommandInfo instances' 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 CommandName (String) or tsItem (ToolStripItem). This is the default property.
  • CommandDictionary gets a Dictionary of MDICommandInfo instances, representing all participating commands. The key is the command-name String; the value is the status-text String.
  • AddItem adds a ToolStripItem to the command dictionary; if the item is a ToolStripDrownDownItem, then AddChildItems is called to handle the drop-down list.
  • AddChildItems adds all ToolStripItems inside a Control, ControlCollection, or ToolStripItemCollection; this method is recursive, and is automatically invoked by the constructor for the entire form.
  • GetCommandsFromResources or 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 ResourceManager or 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 (False).
  • CommandClicked--fired when a participating ToolStripItem (Tag.ToString is not null) is clicked
  • CommandSelected--fired when it's selected (highlighted)
  • CommandDeselected--fired when it's deselected

    MDICommandHandlerEventArg Parameters (all 3 events):

    • e.CommandName = name of command
    • e.CommandItem = specific ToolStripItem in question (e.CommandName = e.CommandItem.Tag.ToString)


  1. When a form or control is searched for menus and tooltips, context menus are no longer skipped. Previously, one need to use the main class' AddChildItems method with a context-menu instance as its argument. Now any form/control, and any of its child controls, featuring a non-null ContextMenuStrip will be included in the search.
  2. If you want an individual ToolStrip item to be omitted from the dictionary, then leave its Tag.ToString value null.
  3. Any showing or hiding of status-bar text occurs before the CommandClicked, CommandSelected, or CommandDeselected event is fired.
  4. 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 "This Command").
  5. If the StatusLabelText property of a command's MDICommandInfo instance is set to a non-null String after MDICommandHandler has been instantiated or the most recent call to GetCommandsFromResources / GetCommandsFromDictionary, then the StatusLabelText value overrides any pre-defined text for that command.
Imports MDICommandSupport

'   constructor
Dim mch As MDICommandHandler = _
   New MDICommandHandler(Me, StatusLabel, Resource3)
Dim mch As MDICommandHandler = _
   New MDICommandHandler(Me, StatusLabel, Dictionary1)

'   properties
mch.CommandInfo("Save").IsCommandEnabled = ShouldWeSave
Dim CanWeSave As Boolean? = mch.CommandInfo("Save").IsCommandEnabled
mch("Open").SetProperty("ForeColor", Color.Red) ' CommandInfo is default property
mch("New").StatusLabelText = "THIS COMMAND has manually set text"

'   methods
mch.GetCommandsFromDictionary(Dictionary2, False)
mch.AddChildItems(Me) : mch.AddChildItems(Me.ContextMenuStrip) ' more controls

'   events
AddHandler mch.CommandClicked, AddressOf mch_CommandClicked

Private Sub mch_CommandClicked(sender As Object, e As MDICommandHandlerEventArgs)
'   preliminary stuff
'   command-specific stuff
Select Case e.CommandName
  Case "Open"
  Case "New"
  Case "Close"
  Case "Save"
End Select
'   final stuff
End Sub


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Robert Gustafson
United States United States
No Biography provided

Comments and Discussions

Generalany C# version available? Pin
Southmountain1-Nov-19 8:59
MemberSouthmountain1-Nov-19 8:59 
GeneralRe: any C# version available? Pin
phil.o2-Nov-19 21:20
mvephil.o2-Nov-19 21:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Posted 11 Feb 2015


18 bookmarked