Log in

No account? Create an account

CAB Command Problems

While designing and developing the Ribbon Gallery support I ran into a significant issue dealing with CAB Commands. My test functionality for gallery support is to mimic the skins gallery in the Ribbon Simple Pad. The first step is to accomplish this within the menu/toolbar paradigm and ensure that it works properly without the galleries.

A good implementation is to iterate over all skins in the SkinManager and dynamically insert them into the menu/toolbar. Each of the buttons/menu items would be linked to the same CommandHandler and pass the name of the skin to display. Here is the problem: Commands are designed to completely decouple the CommandHandler from the UI element; the Command intentionally discards all information about the UI element that fired the Command. There is no method to pass the information assigned to the UI element (in this case the skin name) to the CommandHandler. All of the research that I conducted pointed to either using the EventBroker or the WorkItem State information. However, as far as I can tell, there is no way to get the information from the dynamically created button into the EventBroker or State either.

OK, skins are a controlled, known entity. I can enumerate each skin and create a separate Command and CommandHandler. When Developer Express adds a skin I can just add a new Command. But what about other dynamic elements? One of the other examples in the Ribbon Simple Pad is a gallery of fonts. Fonts are an uncontrolled entity with each computer having it’s own unique font set. Back to figuring out how to handle dynamically generated UI elements.

We could use Reflection.Emit to create a dynamic assembly with each of the skins or fonts. Or it might be possible to use the DynamicMethod class to emit the CommandHandler methods. But if the DevLynx.ShellApp is going to be a general purpose tool then we don’t want to force this complexity on future developers with their own dynamic content. I spent about three days and walked numerous miles contemplating this problem without results.

The solution that the CABDevExpress.Extensions BankTeller application employs is to ignore the UI Extension sites completely. It creates a SkinMenu object which loads the menu items via the normal BarSubItem.AddItem method. But I wanted to use a more generic method if possible. I could always fall back on this solution if needed

The solution that I finally employed is specific to the Developer Express tool set. It also bypasses the CAB Command structure - perhaps also bypassing some of the more burdensome decoupling. I first created a DynamicCommandEventLink class which has two properties; an EventTopicName string and a Data object. These properties are set when the UI element is added to the UI Extension site. The DynamicCommandEventLink instance eventually is attached to the BarItem Tag property. The DevEx Ribbon and BarManager have an event which is fired when any BarItem is clicked. The following code is attached to this event:

   private void ribbonControl_ItemClick(object sender, ItemClickEventArgs e)
       if (e.Item.Tag != null && e.Item.Tag is DynamicCommandEventLink)
           DynamicCommandEventLink eventLink = e.Item.Tag as DynamicCommandEventLink;
           EventTopic eventTopic = rootWorkItem.EventTopics[eventLink.EventTopicName];
           if (eventTopic != null)
               EventArgs<DynamicCommandEventLink> ex = 
new EventArgs<DynamicCommandEventLink>(eventLink);
               eventTopic.Fire(sender, ex, null, PublicationScope.Global);
Here we check to see if the Tag is a DynamicCommandEventLink; if it is, extract the EventTopicName and fire the event passing the data to the event. We bypass the extremely decoupled Commands and are include a coupling to the DevExpress controls. We are already tightly coupled to DevExpress so this appears to be a fairly good compromise. If anyone has a better idea on how to do this please let me know; even a better compromise would be nice.

This works fine for the menu/toolbar and non gallery ribbon items. We will have to see how it works for the galleries when I get the gallery UI adapters complete.