Monday, 7 May 2007

CAB - UIExtensionSite

"UIExtensionSites are the shared containers that can contain your UIElements. Out of the box CAB provides support for MenuStrips and ToolBarStrips but you can easily add support for other elements by implementing your own UIElementAdapter"

I took this explanation from Szymon Kobalczyk's Blog.
You can view the original post here:
http://geekswithblogs.net/kobush/archive/2006/01/24/66946.aspx

The problem that I've found with UIExtensionSites (and it seems a lot of other developers have run into this problem too) is that you need to have a reference the UI item class.

For example:
After registering a menustrip with the ID of "menuBar" I want to add a log in button.
ToolStripMenuItem loginButton = new ToolStripMenuItem("Login");
UIExtensionSites["menuBar"].Add(loginButton);

You can see that we obviously need a reference to the ToolStripMenuItem class, while the MenuBar Add function is handled by CAB.
This will cause problems when at a later date, when I want to update this simple toolstrip menu to an Infragistics powered menu. I will have to go through and modify each and every control that add MenuItems to the extension site.
This just contradicts the reasons why I decided to use CAB - to develop applications using independent modules.

I have known about this limitation for a while, but kept thinking "I'll deal with it when it becomes a problem". Today, it did. I've been working for a couple of weeks on create a docking workspace and getting the user interface working properly and it was almost ready to be used when I was asked to display documents as popup windows instead of the docked panels.

I don't want to remove the docking workspace from the project. The application is built to be used by different divisions within the same company, all of which have different tasks to perform. I don't think the popup window would be suitable for all divisions so I want to leave it as an option. And also, I don't want to see all that development time wasted.

The problem with the popup windows is that the toolbar system I was using before just won't work well with popups. So I will have to change this, BUT if I'm going to leave the docking workspace in as an option for users then I want to use the current toolbars instead.

So what I need is a single menu item control that can be added to any extension site and integrated seamlessly into the UI, no matter what type of menu control the extension site is registered to.

While researching, I found some other people had run into the same problem and pointed out another limitation.
We could use some generic class (instead of ToolbarMenuItem) to define the name, description, icon and click events etc of the menu item, and create an ExtensionSite that reads this definition and turns it into a menu control.
This would work fine for most simple toolbars and we could add/remove/swap toolbars without having to modify the individual modules.

But what about the advanced controls contained in 3rd party UI suites? For example, we want to add a Color Picker - but this isn't supported by all types of toolbars.
I'm sure theres hundreds of similar examples. We could add a generic color picker class and have it display as a textbox in unsupported menus.
But it would be impossible to accommodate for every type of menu item.

What is common among all types of menu?
They all expose some kind of Click event.

Using the color picker example, we could do something like this:
Create the interface to map the 3rd party menu to the generic UIExtensionSite.
Create the generic MenuItem, but tell it we want to pass an instance of one of the native .NET classes to the Click EventHandler - in this case: System.Drawing.Color.

In the ExtensionSite interface, we see that this button is looking for System.Drawing.Color, and knowing that the 3rd party menu supports it - displays a Color Picker.
Other menus will see this and because they don't support System.Drawing.Color, they will find another way to display this - by checking the TypeConverterAttribute and converting to a type that they do support.

This is just a quick idea I've come up with. I find it easier to solve problems while writing my thoughts down.

I still haven't tried implementing it yet but I am planning to. Expect to see a new post in the next couple of days.

1 comment:

Kent Boogaart said...

I am planning a shell at the moment that must be resilient to changes in third party controls and even a switch to WPF. My thinking is to use services to abstract this away from modules. Instead of modules dealing with UIElements directly, they use a service to get their work done. That way only the service needs to change when moving to a new control library.

Obviously you would need to decide what features your UIElements need to support and incorporate those decisions into the design of your services. I think this will work pretty well though.