Creating *actual* custom commands with BaseToolbar

06-09-2014 05:06 AM
Occasional Contributor

I am in the process of creating a toolbar containing nested menus with buttons and other menus for access of some custom functionality for end users. 

For whatever reason, since the ArcMap addin templates do not seem to allow a toolbar contained menus with nested submenus, I turned to the non-addin approach of creating a BaseToolbar which has the ability to add commands (menus, buttons, etc.):

public MyToolbar()

where ButtonItem is a class which inherits BaseCommand:

public class ButtonItem : BaseCommand
     // ...

What I really want these buttons to do is have some non-ESRI functionality (which none of the examples provide), for example, when clicked, retrieves some id, name, or caption of this button, and traverses a directory on the file system to add a new layer to the map, or create a config .xml file somewhere on the file system.

However, there seems to be no room to add any custom methods or properties to ButtonItem, since the method AddItem() of the base toolbar does not take an instantiated object, but either a progid, or class type. 

How can I handle the onClick method of this specific button to access class properties?  There does not seem to be a clear indication of how/when the constructor and onCreate methods are executed.

Does anyone have experience utilizing this toolbar/menu style, or know how to dynamically add nested menu items to an add-in toolbar (loop over a directory, and add a menu item for a folder, or a button item for a file)?
0 Kudos
3 Replies
Regular Contributor III
I guess I'm not following your definition of the problem.  A custom command can do whatever you want it to do.  Just add whatever code you need to the OnClick event.  When the command is clicked, that code executes.
0 Kudos
Occasional Contributor II

Instead of deriving your custom toolbar from BaseToolbar in the ESRI.ArcGIS.ADF namespace, have you considered implementing the ICommandBar interface?  Any custom tools/buttons/menus you want to add to it would need to implement the ICommand interface.

Regarding the OnCreate event, the help documentation for ICommand says this:

Commands are constructed once initially to get information about them, like the name, bitmap, etc and then they are destroyed.  When the final, complete construction takes place, the OnCreate method gets called.  OnCreate gets called only once, so you can rely on it to perform initialization of member variables.  You can check for initialized member variables in the class destructor to find out if OnCreate has been called previously.

Regarding "nested menus," the IToolPalette may be what you're looking for.
0 Kudos
New Contributor III
Kevin, hopefully this isn't too little too late for you, but here's what you actually need to do.

Go back to using the standard add-in toolbar setup. (Unless there's some other reason to create your own that you didn't mention. This will still work with that as well, it's just easier to work with the Config.esriaddinx setup than defining custom classes, IMHO.) Then add a Menu to the Toolbar. Then add a new Add-in Component and select Multi-Item. Fill in the details about your Multi-Item (it doesn't really make much difference as far as I've been able to tell what you put there, but just in case...) and then add it to your Menu.

Finally, open the .cs/.vb file and you'll need to override two methods: (the project that I used this method on was written in VB.Net, so I'll just use that for my examples even though I've grown more fond of C#...)

Protected Overrides Sub OnPopup(ByVal items As ESRI.ArcGIS.Desktop.AddIns.MultiItem.ItemCollection)

Protected Overrides Sub OnClick(ByVal item As ESRI.ArcGIS.Desktop.AddIns.MultiItem.Item)

OnPopup happens when your Menu is opened and what it's expecting you to do is add Items to the ItemCollection that is passed in. The Item class is very simple and provides things similar to a Command. But you don't need to subclass it and you probably don't need to worry about setting anything other than the Caption. In my code, I have each of my possible Items defined as private members and I just add the ones I need, depending on certain conditions, in the call to OnPopup. Then in the OnClick call, I check to see which one of my Items was returned to me as the one that was clicked and do whatever needs to be done based on that.

In your case, it sounds like the Items are essentially populated using your file structure. In that case, you might want to have some other piece of information in addition to the Caption, like the full path as opposed to a more friendly version of the file name. So let me give a quick example of what you could do:

Protected Overrides Sub OnPopup(ByVal items As ItemCollection)
    Dim files As List(Of System.IO.FileInfo) = 'Some method that returns a collection of FileInfo objects in the desired path(s)
    For Each file In files
        items.Add(New Item With {.Caption = file.Name, .Tag = file.FullName})
End Sub

Protected Overrides Sub OnClick(ByVal item As Item)
    Dim file As New System.IO.FileInfo(item.Tag)
    'Code to open the file and do something with it
End Sub

The possibilities of what you want to do with it from there are nearly endless. You can then have as many or as few items show up as you want (I think), but I would suggest you do something to keep it from being several hundred items all at once... 😉

Hope that helps you and/or someone else! 🙂
0 Kudos