How to create multiple commands or tools in a single class subtyped command


Summary
The ICommandSubtype interface allows you to implement multiple commands or tools in a single class. Instead of implementing commands or tools with similar functionalities in separate classes, use subtyped commands to share and maintain code among these commands or tools.


Creating multiple commands or tools in a single class

By implementing ICommandSubType, you can create a custom subtyped command or tool similar to creating a custom command or tool. The difference is that you create one class, which can act as many different commands or tools, instead of having to create and manage a number of individual classes. It is particularly useful if you have a number of commands or tools performing similar tasks and sharing significant amounts of code.
Since multiple commands or tools are represented in a single class, each subtyped command or tool is identified by a subtype value (value starts from 1) in addition to its class identifier (CLSID). When defining the items on a toolbar or menu, specify the subtype code in the unique identifier object (UID) appropriately.
A subtyped command or tool is different than a MultiItem, shown in How to create dynamic menu commands using a MultiItem. A subtyped command or tool has a fixed number of items, while the number of items in a MultiItem is dynamic (which can appear as zero or more adjacent menu items) depending on the application state.

Adding subtyped commands

Do the following steps to add a subtyped command implementation to an existing project:
  1. Create a command or tool class, detailed in the topic Creating commands and tools. You can implement the ICommand or ITool interfaces, or inherits from BaseCommand or BaseTool. You can also use the ArcGIS Base Command or Base Tool Item template to start your implementation. The class in this topic inherits from BaseCommand.
  2. When creating a subtyped command or tool, it is easier to implement the straightforward ICommandSubType interface first.
    • Once the class is set up in the project, implement ICommandSubType.
See the following code example that shows how to implement a series of commands as subtypes that perform fixed zoom in, from one to three times, when the command is clicked:
[C#]
public sealed class ZoomInCommands: BaseCommand, ICommandSubType
[VB.NET]
Public NotInheritable Class ZoomInCommands
Inherits BaseCommand
Implements ICommandSubType
  1. The ICommandSubType interface contains two methods—GetCount and SetSubType. Add implementation to the GetCount method by returning the number of commands this class contains. The ZoomInCommands class returns 3.
  2. The most important method in ICommandSubType is SetSubType. A separate instance of the subtyped command or tool is created for each required item, and its subtype is set after instantiation. The subtype of any instance remains throughout the instance's lifetime but varies from instance to instance. Save the value, which is passed to SetSubType, as a member variable so that you can complete the members of ICommand and ITool appropriately. See the following code example:
[C#]
private int m_subtype;
... public void SetSubType(int SubType)
{
    m_subtype = SubType;
    ...
}
[VB.NET]
Private m_subtype As Integer
...

Public Sub SetSubType(ByVal SubType As Integer) Implements ESRI.ArcGIS.SystemUI.ICommandSubType.SetSubType
    m_subtype = SubType
    ...
End Sub
  1. Set up command or tool properties. Use a case statement to determine the subtype being queried; apart from that, complete the members as you would for any other command. For example, return a different bitmap depending on the current subtype. See the following code example:
[C#]
public ZoomInCommands()
{
    //Set up common properties.
    base.m_category = "My Command";
}

... public void SetSubType(int SubType)
{
    //Called by framework to indicate the subtype command the application is referring to.
    //Subtype value starts from 1.
    m_subtype = SubType;

    //Set up bitmap, caption, ToolTip, and so on.
    if (base.Bitmap == 0)
    //Load from project resource.
    {
        switch (m_subtype)
        {
            case 1:
                base.m_bitmap = Properties.Resources.ZoomOnce;
                break;
            case 2:
                base.m_bitmap = Properties.Resources.ZoomTwice;
                break;
            case 3:
                base.m_bitmap = Properties.Resources.ZoomThrice;
                break;
        }
    }
    base.m_caption = string.Format("Fixed zoom in x{0} (C#)", m_subtype.ToString());
    base.m_name = string.Format("MyCommand_ZoomCommandx{0}CS", m_subtype);
    base.m_message = string.Format("Executing fixed zoom in {0} time(s)",
        m_subtype.ToString());
    base.m_toolTip = string.Format("Fixed Zoom in x{0}", m_subtype);
}
[VB.NET]
Public Sub New()
    MyBase.New()
    'Set up common properties.
    MyBase.m_category = "My Command"
End Sub

...

Public Sub SetSubType(ByVal SubType As Integer) Implements ESRI.ArcGIS.SystemUI.ICommandSubType.SetSubType
    'Called by framework to indicate the subtype command the application is referring to.
    'Subtype value starts from 1.
    m_subtype = SubType
    
    'Set up bitmap, caption, ToolTip, and so on.
    If MyBase.Bitmap = 0 Then 'Load from project resource.
        Select Case m_subtype
            Case 1
                MyBase.m_bitmap = My.Resources.ZoomOnce
            Case 2
                MyBase.m_bitmap = My.Resources.ZoomTwice
            Case 3
                MyBase.m_bitmap = My.Resources.ZoomThrice
        End Select
    End If
    MyBase.m_caption = String.Format("Fixed zoom in x{0} (VB.NET)", m_subtype.ToString())
    MyBase.m_name = String.Format("MyCommand_ZoomCommandx{0}VB", m_subtype)
    MyBase.m_message = String.Format("Executing fixed zoom in {0} time(s)", m_subtype.ToString())
    MyBase.m_toolTip = String.Format("Fixed Zoom in x{0}", m_subtype)
End Sub
  • Only load resources, command bitmaps for example, that are needed by the particular subtype. Avoid loading resources in the class constructor; instead, delay resource loading until the subtype is determined or when the property is first accessed.
  • The bitmaps in the previous code example are added as project resources.
  1. Complete other members of the command or tool. Generally, the OnCreate method can be coded regardless of subtype (similar for the Enabled property). See the following code example:
[C#]
private IApplication m_application;
... public override void OnCreate(object hook)
{
    if (hook == null)
        return ;
    m_application = hook as IApplication;
    //Disable if it is not ArcMap.
    if (hook is IMxApplication)
        base.m_enabled = true;
    else
        base.m_enabled = false;
}
[VB.NET]
Private m_application As IApplication
...
Public Overrides Sub OnCreate(ByVal hook As Object)
If Not hook Is Nothing Then
    m_application = CType(hook, IApplication)
    'Disable if it is not ArcMap
    If TypeOf hook Is IMxApplication Then
        MyBase.m_enabled = True
    Else
        MyBase.m_enabled = False
    End If
End If
End Sub
  1. For commands, handle the OnClick method by the current subtype. The subtype value in the following code example determines the number of times the fixed zoom in command is executed:
[C#]
public override void OnClick()
{
    UID cmdUID = new UIDClass();
    cmdUID.Value = "esriArcMapUI.ZoomInFixedCommand";
    ICommandBars docBars = m_application.Document.CommandBars;
    ICommandItem xoomCommand = docBars.Find(cmdUID, false, false);
    if (xoomCommand != null)
    {
        for (int i = 0; i < m_subtype; i++)
            xoomCommand.Execute();
    }
}
[VB.NET]
Public Overrides Sub OnClick()
Dim cmdUID As New UIDClass()
cmdUID.Value = "esriArcMapUI.ZoomInFixedCommand"
Dim docBars As ICommandBars = m_application.Document.CommandBars
Dim xoomCommand As ICommandItem = docBars.Find(cmdUID)
If xoomCommand IsNot Nothing Then
    For i As Integer = 1 To m_subtype 'Starts from 1
        xoomCommand.Execute()
    Next
End If
End Sub
  1. If implementing a tool, handle the mouse and key events appropriately.
As shown in the previous code example, one advantage of creating similar commands as a subtyped command is that you can share the basic code between the commands. All command subtypes share the same code to get the fixed zoom in command.
  1. Register the subtyped command or tool into the appropriate component categories. You can use the ArcGIS Component Category Registrar dialog box dialog box to insert the registration code. The previous class works in ArcMap and should be registered in the ArcMap Commands (MxCommands) component category.


See Also:

Creating commands and tools




Development licensing Deployment licensing
ArcGIS for Desktop Basic ArcGIS for Desktop Basic
ArcGIS for Desktop Standard ArcGIS for Desktop Standard
ArcGIS for Desktop Advanced ArcGIS for Desktop Advanced