Quantcast
Channel: Net Knowledge
Viewing all articles
Browse latest Browse all 25

Workaround for Injecting menu items into CAB Shell MenuStrip

$
0
0

Anyone who has worked with CAB for a while has probably been bitten by a limitation of the UIExtensionSite when adding menu items into the shell from a module. The limitiation is that the UIExtensionSite only supports the Add method to add additional items to a UI element. Translated to a MenuStrip example, that means that you can only add menu items to the shell's menu. For example, in your shell you define the File menu with an Exit item. Registering the File menu as an extension site, you can now add menu items to the File menu and they will be added after the Exit item. The results are probably not what you want. What can you do if you want to insert new items before the Exit item? Read on.

 The key to working around the problem is to realize that you have a great deal of flexibility in which UI elements you expose as extension sites. For a MenuStrip in the shell there are three basic choices for what to expose. I will call them Top level, Detail level and Individual item.

Expose the Top level menu items if you want to add more top level menu items from a module. For example if you have File and Edit menu itmes and you want to add a top level menu called "My Menu" then register the top level like this:

RootWorkItem.UIExtensionSites.RegisterSite(UIExtensionSiteNames.MainMenu, this.Shell.MainMenuStrip.Items);

In your module, add the new top level menu item.

UIExtensionSites[UIExtensionSiteNames.MainMenu].Add(new ToolStripMenuItem("My Menu"));

"My Menu"  will appear as the rightmost top level menu.

Expose a detail level menu collection to add additional menu items after any items that are already defined in the shell. For example if you have a File menu with an Exit item then registering it like this

ToolStripMenuItem fileItem = (ToolStripMenuItem)Shell.MainMenuStrip.Items[0];
RootWorkItem.UIExtensionSites.RegisterSite(UIExtensionSiteNames.FileMenu, fileItem.DropDownItems);

and adding a menu item like this

UIExtensionSites[UIExtensionSiteNames.FileMenu].Add(new ToolStripMenuItem("New"));

will yield a File menu with Exit then New.

So it seems you can only add items to the end of the list! What if you wanted that New menu item to be placed before the Exit item? Ok, here the trick. The third option is to register an invisible individual menu item as the first menu item in the File menu. When you register this individual item and Add to it, items will appear after it in the list. So the registration code looks like this:

             //need a placeholder menu item to add items after
            ToolStripSeparator placeHolderTool = new ToolStripSeparator();
            placeHolderTool.Visible = false;
            fileItem.DropDownItems.Insert(0,placeHolderTool);
            RootWorkItem.UIExtensionSites.RegisterSite(UIExtensionSiteNames.FileMenu, placeHolderTool);

Now when you add the New menu item

UIExtensionSites[UIExtensionSiteNames.FileMenu].Add(new ToolStripMenuItem("New"));

You get a File menu with with New then Exit as a result. 

The little magic that makes this possible is in the ToolStripUIAdapterFactory. It looks at what kind of ui element you are exposing and creates the correct adapter for it. The adapter knows how to handle each situation and performs the correct implementation for you.

    public class ToolStripUIAdapterFactory : IUIElementAdapterFactory
    {
        /// <summary>
        /// See <see cref="IUIElementAdapterFactory.GetAdapter"/> for more information.
        /// </summary>
        public IUIElementAdapter GetAdapter(object uiElement)
        {
            if (uiElement is ToolStrip)
                return new ToolStripItemCollectionUIAdapter(((ToolStrip)uiElement).Items);

            if (uiElement is ToolStripItem)
                return new ToolStripItemOwnerCollectionUIAdapter((ToolStripItem)uiElement);

            if (uiElement is ToolStripItemCollection)
                return new ToolStripItemCollectionUIAdapter((ToolStripItemCollection)uiElement);

            throw new ArgumentException("uiElement");
        }
...


Viewing all articles
Browse latest Browse all 25

Trending Articles