How to create dynamic menu commands using a MultiItem


Summary
A MultiItem can be used when commands on a menu cannot be determined before run time or the commands need to be modified based on the state of the system.


Creating dynamic menu commands

By implementing IMultiItem, a single class can act like several adjacent menu commands. During run time, the framework notifies MultiItems when their host menu is about to be shown and how each sub-item should appear. A good example of this is the menu commands at the bottom of the File menu representing the most recently used files.
Besides IMultiItem, your class can implement the optional IMultiItemEx interface. Unlike other commands and tools, you do not implement the ICommand interface when creating a MultiItem. MultiItems are never added to the commands list on the Customize dialog box, they can only appear on menus; therefore, create a custom menu to expose them in the user interface. For more information, see Creating toolbars and menus.
A MultiItem is different than a subtyped command or tool. A subtyped command or tool has a fixed number of items and can be exposed on the Customize dialog box. For more information, see How to create multiple commands or tools in a single class subtyped command.

Implementing a MultiItem

Do the following steps to implement a MultiItem. The accompanied code example demonstrates how to create multiple menu commands, each one corresponding to a layer to which the map zooms in when the menu command is clicked:
  1. Create a class that implements IMultiItem. Use the ArcGIS Add Class Wizard to do this by selecting Application Framework on the Customization Group drop-down list and MultiItem from the Base Component list. For more information, see Using the ArcGIS Add Class Wizard to extend ArcObjects.
  2. Implement the Caption, HelpContextID, HelpFile, Message, and Name properties as you would for any command.
The Caption property returns the placeholder string representing the MultiItem when the host application is in customized mode (for example, when the Customize dialog box is opened in ArcMap). The framework encloses the caption with {} to indicate that it is a MultiItem.
  1. The main concept to understand about implementing IMultiItem is the OnPopup method. This method occurs before the menu containing the MultiItem appears and provides two important parts of functionality. First, it provides the hook to the object that instantiated the MultiItem and second, it returns the number of items in the MultiItem. Before the MultiItem is shown, all the CommandItems that contain a MultiItem can query the OnPopup method to determine how many sub-items display.
  2. After the OnPopup method is called, the framework creates a CommandItem for each sub-item and the ItemBitmap, ItemCaption, ItemChecked, and ItemEnabled indexed properties are used to determine the details of each sub-item. For these properties, the index value passed in represents the sub-item number. From these properties, return an appropriate value for each sub-item.
  3. Finally, after the MultiItem displays, the user can select an item, which calls the OnItemClick method, again passing in the appropriate sub-item number.
  4. IMultItemEx is an optional interface for MultiItems, which offers additional indexed properties. By implementing this interface, you can have a separate message, Help file, and Help context ID for each sub-item.
  5. Register the class into the appropriate command component categories. Here, the sample class can be registered in the ESRI Mx Commands or ESRI Controls Commands categories. For more information, see Registering classes in COM component categories.
  6. To show the MultiItem in your application, add it to a custom menu implementation.

    The following code example shows a MultiItem class implementation that works in ArcMap, MapControl, or PageLayoutControl:
[C#]
namespace HowToCodeSnippets
{

    [Guid("4e0f22f3-5619-4b75-b9b0-36ea859db97d")][ClassInterface
        (ClassInterfaceType.None)][ProgId("HowToCodeSnippets.HowToMultiItem")]
    public class HowToMultiItem: IMultiItem, IMultiItemEx
    {
        private ESRI.ArcGIS.Controls.IHookHelper m_hookHelper;

        #region "IMultiItem Implementations"

        //Step 2.
        public string Caption
        {
            get
            {
                return "Layer Zoom";
            }
        }
        public string Name
        {
            get
            {
                return "CSNETSamples_HowToMultiItem";
            }
        }
        public int HelpContextID
        {
            get
            {
                return 0;
            }
        }
        public string HelpFile
        {
            get
            {
                return "";
            }
        }
        public string Message
        {
            get
            {
                return "MultiItem single message";
            }
        }

        //Step 3.
        public int OnPopup(object hook)
        {
            m_hookHelper = new ESRI.ArcGIS.Controls.HookHelperClass();
            m_hookHelper.Hook = hook;
            return m_hookHelper.FocusMap.LayerCount;
        }

        //Step 4.
        public string get_ItemCaption(int index)
        {
            ESRI.ArcGIS.Carto.ILayer layer = m_hookHelper.FocusMap.get_Layer(index);
            return "Zoom to " + layer.Name;
        }
        public bool get_ItemEnabled(int index)
        {
            //Enable zoom in only when the layer is visible.
            ESRI.ArcGIS.Carto.ILayer layer = m_hookHelper.FocusMap.get_Layer(index);
            return layer.Visible;
        }
        public int get_ItemBitmap(int index)
        {
            return 0; //Not implemented.
        }
        public bool get_ItemChecked(int index)
        {
            return false;
        }

        //Step 5.
        public void OnItemClick(int index)
        {
            //Get the layer and its spatial referenced extent.
            ESRI.ArcGIS.Carto.ILayer layer = m_hookHelper.FocusMap.get_Layer(index);
            ESRI.ArcGIS.Geometry.IEnvelope env = layer.AreaOfInterest;
            //Zoom to the extent and refresh the view.
            ESRI.ArcGIS.Carto.IActiveView activeView = m_hookHelper.FocusMap as
                ESRI.ArcGIS.Carto.IActiveView;
            activeView.Extent = env;
            activeView.Refresh();
        }
        #endregion 

        //Step 6–Optional interface implementation.
        #region "IMultiItemEx Implementations"
        public string get_ItemMessage(int index)
        {
            ESRI.ArcGIS.Carto.ILayer layer = m_hookHelper.FocusMap.get_Layer(index);
            return "Zoom to " + layer.Name + "layer.";
        }
        public int get_ItemHelpContextID(int index)
        {
            return 0;
        }
        public string get_ItemHelpFile(int index)
        {
            return "";
        }
        #endregion 
    }
}
[VB.NET]
<ComClass(LayerZoomMultiItem.ClassId, LayerZoomMultiItem.InterfaceId, LayerZoomMultiItem.EventsId), _
          ProgId("MultiItemHowToVB.LayerZoomMultiItem")> _
          Public Class LayerZoomMultiItem
    Implements IMultiItem
    Implements IMultiItemEx
    
    #Region "COM GUIDs"
    Public Const ClassId As String = "0679a7e9-7e4e-42d9-975b-4d2437f0882a"
    Public Const InterfaceId As String = "7b6829ef-9909-4d9d-94aa-9a0b0820bf69"
    Public Const EventsId As String = "f8d38bca-6c48-4c6a-92d1-9365cfc40fe9"
    #End Region
    
    Private m_hookHelper As ESRI.ArcGIS.Controls.IHookHelper
    
    Public Sub New()
        MyBase.New()
    End Sub
    
    'Step 2.
    Public ReadOnly Property Caption() As String Implements IMultiItem.Caption
    Get
    Return "Layer Zoom"
    End Get
End Property

Public ReadOnly Property Message() As String Implements IMultiItem.Message
Get
Return "Zoom to layer"
End Get
End Property

Public ReadOnly Property Name() As String Implements IMultiItem.Name
Get
Return "VBNETSamples_HowToMultiItem"
End Get
End Property

Public ReadOnly Property HelpContextID() As Integer Implements IMultiItem.HelpContextID
Get
Return 0
End Get
End Property

Public ReadOnly Property HelpFile() As String Implements IMultiItem.HelpFile
Get
Return ""
End Get
End Property

'Step 3.

Public Function OnPopup(ByVal hook As Object) As Integer Implements IMultiItem.OnPopup
    m_hookHelper = New ESRI.ArcGIS.Controls.HookHelperClass()
    m_hookHelper.Hook = hook
    Return m_hookHelper.FocusMap.LayerCount
End Function

'Step 4.
Public ReadOnly Property ItemCaption(ByVal index As Integer) As String Implements IMultiItem.ItemCaption
Get
Dim layer As ESRI.ArcGIS.Carto.ILayer = m_hookHelper.FocusMap.Layer(index)
Return "Zoom to " + layer.Name
End Get
End Property

Public ReadOnly Property ItemEnabled(ByVal index As Integer) As Boolean Implements IMultiItem.ItemEnabled
Get
'Enable zoom in only when the layer is visible.
Dim layer As ESRI.ArcGIS.Carto.ILayer = m_hookHelper.FocusMap.Layer(index)
Return layer.Visible
End Get
End Property

Public ReadOnly Property ItemChecked(ByVal index As Integer) As Boolean Implements IMultiItem.ItemChecked
Get
Return False
End Get
End Property

Public ReadOnly Property ItemBitmap(ByVal index As Integer) As Integer Implements IMultiItem.ItemBitmap
Get
Return 0
End Get
End Property

'Step 5.

Public Sub OnItemClick(ByVal index As Integer) Implements IMultiItem.OnItemClick
    Dim layer As ESRI.ArcGIS.Carto.ILayer = m_hookHelper.FocusMap.Layer(index)
    Dim env As ESRI.ArcGIS.Geometry.IEnvelope = layer.AreaOfInterest
    'Zoom to the extent and refresh the view.
    Dim activeView As ESRI.ArcGIS.Carto.IActiveView = DirectCast(m_hookHelper.FocusMap, ESRI.ArcGIS.Carto.IActiveView)
    activeView.Extent = env
    activeView.Refresh()
End Sub

'Step 6–Optional interface implementation.
Public ReadOnly Property ItemMessage(ByVal index As Integer) As String Implements IMultiItemEx.ItemMessage
Get
Dim layer As ESRI.ArcGIS.Carto.ILayer = m_hookHelper.FocusMap.Layer(index)
Return "Zoom to " + layer.Name + "layer."
End Get
End Property

Public ReadOnly Property ItemHelpContextID(ByVal index As Integer) As Integer Implements IMultiItemEx.ItemHelpContextID
Get
Return 0
End Get
End Property

Public ReadOnly Property ItemHelpFile(ByVal index As Integer) As String Implements IMultiItemEx.ItemHelpFile
Get
Return ""
End Get
End Property

End Class


See Also:

Recently used files—Command, MultiItem, and ComboBox
Add a custom bookmarks MultiItem to the ToolbarControl
Creating toolbars and menus
How to create multiple commands or tools in a single class subtyped command
Using the ArcGIS Add Class Wizard to extend ArcObjects
How to register COM components




To use the code in this topic, reference the following assemblies in your Visual Studio project. In the code files, you will need using (C#) or Imports (VB .NET) directives for the corresponding namespaces (given in parenthesis below if different from the assembly name):
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
Engine Developer Kit Engine