Introduction
This article describes a component for routing menu and toolbar item events through a centralized switchboard for easier execution and update. Windows Forms design-time support is also an important part of the software.
Background
I have always found the command update an execution architecture in MFC that is very easy to use. Despite all the good parts of Windows Forms, it is still missing this architecture: an easily isolated mechanism for updating menus and toolbars based on application state. I also wanted to research design-time support in Windows Forms, to see what is possible. So, I wrote this switchboard to provide a similar functionality in Windows Forms.
Description
An application command represents an action that you want to execute in your application. It can be triggered by a UI item, which can be a menu item, toolbar item or come directly from code. Your application should only handle application command updates and executions, not connect directly to UI items. Changes to a UI item is reflected in the application command and other associated UI items.
An application command can be executed by a UI item. Normally, this is a menu or toolbar item click. However, it can also be a selection change in a combo box or text that is changed in an edit box. Any event in a UI item can be used to execute a command. There can be one global execute handler and/or a specific execute handler for each application command. These commands can also be executed directly from code.
An application command sometimes has to be updated. The update normally involves enable/disable, check/uncheck or show/hide. The update can also be to select an element in a combo box or to change text in an edit box. There can be one global update handler and/or a specific update handler for each application command. The update can be activated for the application command or for each UI item. UI items should be updated when a context menu is shown. By adding the context menu to the context menu collection, all UI items in the context menu are updated when the menu is shown.
UISwitchboard
uses an adapter pattern for supporting different types of menu and toolbar implementations. The adapter implements an interface defined by UISwitchboard
. NET 2.0 Toolstrip menus and toolbars, as well as NET 1.x menus and toolbars, are supported by an adapter for each type. It is easy to write another adapter for third party menu and toolbar packages.
Using the Code
Adding Components to Toolbox
To use the Switchboard component, you have to add the component to the Visual Studio Toolbox by selecting the "Choose Items..." menu item in the toolbox context menu. In the ".NET Framework Components" tab, select the "Browse" button and browse for the UISwitchboard.dll assembly. Two new components, ToolStripUISwitcboard
and MenuToolBarUISwitchboard
, should now appear in the toolbox under the "Menus & Toolbars" group.
Adding Components to Form
You can now add the component to a Windows Form. Simply drag the component from the toolbox and drop the component on the surface of the form. The component is now added to the form and is visible below the form with all the other components you have added. You can now change the name and other properties on the component. A reference to the UISwitchboard
assembly is added to the project by this action.
Figure 1: Component in form
Creating Application Commands
The next step is to define all application commands. These commands should be a logical set of commands that you will execute in the application. You can use either the smart tag menu on the component or the component properties to open the "Application Command Collection Editor." This editor is used to add, delete or edit an application command.
Figure 2: Editing application commands
You can also create application commands directly from your code by using the properties and methods on the UISwitchboard
classes.
Associating UI Items with a Command
Application commands are triggered by UI items. Items have to be associated with a command to be able to trigger execution of the command. This association can be edited from the application command properties by opening the "UIItems
" collection. This will open the "UIItem Associations Editor." This editor shows all available items and which items are associated with the current command. You can now add or remove associations between items and the current command.
Figure 3: Edit UI item associations
You can also edit associations by selecting a UI item and using the "ApplicationCommand
on xx" property for the item. Select the application command you want to associate from the drop down list or "(none)" if you want to remove the association.
Figure 4: Edit UI item associations using property extender
Handling Execute and Update
To handle execute and update events, add event handlers to the "ApplicationCommandExecute
" and "ApplicationCommandUpdate
" events on the UISwitchboard
component. These event handlers receive an "ApplicationCommandEventArgs
" argument that contains information about the command and item that triggered the event. By checking these properties, you can update the command or item and execute the appropriate methods in your code. The code example below shows a generic execution handler, which displays available information in a message box when the command is executed.
private void toolStripExtensionCommandSwitchboard1_CommandExecute(
object sender, ApplicationCommandEventArgs e)
{
Control owner = sender as Control;
ApplicationCommand command = e.ApplicationCommand;
ToolStripItem item = e.Item as ToolStripItem;
string ownerName = "null";
if (owner != null)
{
ownerName = owner.Name;
}
string itemName = "null";
if (item != null)
{
itemName = item.Name;
}
System.Windows.Forms.MessageBox.Show("G Executing command:" +
command.ApplicationCommandName + " owner:" + ownerName +
" item:" + itemName);
}
The code example below shows how the update handler updates UI items based on application state.
private void toolStripExtensionCommandSwitchboard1_CommandUpdate(
object sender, ApplicationCommandEventArgs e)
{
Control owner = sender as Control;
ApplicationCommand command = e.ApplicationCommand;
ToolStripItem item = e.Item as ToolStripItem;
switch (command.ApplicationCommandName)
{
case "New":
e.ApplicationCommand.Enabled = checkBoxEnableNew.Checked;
break;
case "FileCopy":
e.ApplicationCommand.Enabled = checkBoxEnableGlobalCopy.Checked;
break;
case "ContextCopy":
e.ApplicationCommand.Enabled = checkBoxEnableLocalCopy.Checked;
break;
case "Save":
e.ApplicationCommand.Visible = checkBoxShowSave.Checked;
break;
case "TrackBar":
toolStripProgressBar1.Value = toolStripTrackBar1.Value * 10;
break;
}
}
You can also add handlers for these events directly on an application command, if it is easier to handle a command in a specific event handler instead of the global event handler.
Associating Context Menus
Context menus need to be associated with the UISwitchboard
component. if you want to update the context menu UI items when the context menu is shown. You have to associate the context menu with the UISwitchboard
. You can use either the smart tag menu on the component or the component properties to open the "Context menus Association Editor." You can now add or remove context menus from the associated list.
Figure 5: Edit context menu associations
.NET 2.0 UI Items Supported
The following UI items are supported for .NET 2.0:
ToolStripItem
ToolStripButton
ToolStripLabel
ToolStripComboBox
ToolStripTextBox
ToolStripProgressBar
ToolStripDropDownItem
ToolStripMenuItem
ToolStripSplitButton
ToolStripDropDownButton
ToolStripStatusLabel
ContextMenuStrip
.NET 1.x UI Items Supported
The following UI items are supported for .NET 1.0 and 1.1:
MenuItem
ToolBarButton
ContextMenu
Extensibility
The switchboard is designed to be extensible by using an adapter pattern. The adapter handles the tasks that require knowledge of the UI item implementations. I have included adapters for the old .NET 1.x menu and toolbar classes, as well as for the .NET 2.0 menu and toolbar classes. It should be easy to write an adapter for a third party menu and toolbar package. Because the design-time support instantiates a component when loading the form, you also have to provide a specific UISwitchboard
derived class which instantiates the correct adapter in the constructor. It is also possible to extend one of the existing adapters for supporting your own custom menu or toolbar controls, in addition to the standard controls. The source code includes an example of this extension.
Design-time Support
The goal for this component was to make it easy to add application commands and connect them to user interface items. The solution is design-time support. The design-time support should automate tasks such as finding all UI items available and presenting them in an easy-to-use user interface.
UISwitchboard Designer
A component with design-time support requires a designer. The UISwitchboardComponentDesigner
class implements the required functionality for supporting the UISwitchboard
component.
Smart tags
Similar to Office smart tags, Visual Studio smart tags make common tasks available that apply to the context of your work. The component designer class provides support for defining the commands that shall be available as smart tag commands.
Application Commands
Application commands are edited through the "ApplicationCommand
collection editor." This design-time form provides support for creation, deletion and editing of application commands.
Figure 6: Application command collection editor
UI Item Associations
Each UI item needs to be associated with an application command. This design-time form provides support for associating a UI item with the current application command.
Figure 7: UIItem association editor
Property Extender
It is also possible to associate an item from the item properties. I use a property extender to add a property to each supported item, which lets you select an application command.
Points of Interest
Two major revelations occurred during this programming exercise, in addition to lots of small ones:
- Design-time objects always need to inherit from the
Component
class. This will enable all design-time support to work as if by magic. Without this inheritance, strange things happen. - Properties of components always need both a
get
and set
method to be serialized at design-time. - I had a hard time finding information about some of the design-time features. When I at last used Reflector to examine the Microsoft
SplitterPanel
component, then everything was easy and I needed only a few lines of code to make everything work.
I have generated a reference manual via Sandcastle and SandcastleBuilder. This is included in the source download.
Terms and Conditions
The UISwitchboard
component is provided as free software with source code provided. You can freely use it in your applications, commercial or non-commercial. Bjørn Sundby, the author of the component, owns the copyright of the component and is not responsible for any damage in your application caused by using this component, directly or indirectly.
History
2007.05.01