CAB - Support for Custom UIExtensionSites

Almost every client I've been working with on CAB projects wanted the look and feel of Microsoft Outlook where you have a navigation pane on the side of the shell host and a content which is changing on the right pane.

So lets see how we can get this cool look and feel in out CAB application...

Searching over the web you might find an Outlook Bar that you might want to use in your CAB application, In my search I found on CodeProject - Herre Kuijpers Outlook Bar and i'll be using it to demonstrate what are the steps needed to integrate it into your CAB application.

 

You might be thinking ahh... you are going to show me that you just drag and drop it to the CAB shell layout and everything is working ... well I wish it was as simple as that.

First thing that will do is to design our Composite UI shell and yep we will drag the Outlook Bar to the shell layout which is the easy step... the hard step is how to expose this part as UIExtensionSite to the modules of the Composite UI Application.

Note: I am going to use the CAB with the Smart Client Software Factory (SCSF) in my demonstrations as I find it to be a silver bullet in most cases.

 

Now we would like to expose the Shell's Outlook Bar as UIExtensionSite so other modules will be able to add buttons and commands on the fly (I.e. while the module initialize)

First lets expose the new Outlook Bar as an Internal Shell View Property:
internal OutlookStyleControls.OutlookBar MainOutlookBar
{
     get { return _mainOutlookBar; }
}

Next we add a constant for the new Outlook Bar UIExtension name and register it as UIExtensionSite as the Shell Layout View Presenter:
protected override void OnViewSet()
{
            WorkItem.UIExtensionSites.RegisterSite(UIExtensionSiteNames.MainOutlookBar, View.MainOutlookBar);
}

You are now probably happy thinking ohh... that was easy but as you run the application you are getting the following exception:

Its time for a little tip: whenever you are working with CAB the exception you will get are most of the time runtime exceptions, its hard to diagnose these exceptions as you have no idea where they came from because most of the exceptions are due to reflection operations inside the Object Builder infrastructure or in other CAB infrastructure. So how do you find easily where the exception came from ?
Ask visual studio to throw the exceptions as they occur and not to bubble them up the stack, you do this by pressing ALT-CTRL-E and choosing to throw CLR Exceptions.

Now when you can stop at the exception you can dig it out... and find out that the problem is that the Object Builder - the infrastructure that creates the instance of the object, doesn't know about the type (OutlookBar) that you want to create and as such throw an exception.

So how do we solve the issue ? We need to introduce to the Object Builder a new Factory that will be used to create the OutlookBar.

What I normally do is open a new project which will host the Adapters for the control which I want to expose as UIExtensionSite this way I'm not touching the control and actually look at it as a black box while supplying the cab infrastructure the ability to create and use the control.

Ok so lets code the OutlookBarAdapterFactory:

public class OutlookBarAdapterFactory : IUIElementAdapterFactory    {
      #region IUIElementAdapterFactory Members
      public IUIElementAdapter GetAdapter(object uiElement)
      {
          if (uiElement is OutlookBar)
              return new OutlookBarUIAdapter((OutlookBar) uiElement);
          throw new ArgumentException("uiElement");
      }
      public bool Supports(object uiElement)
      {
          return (uiElement is OutlookBar);
      }
      #endregion
}

The GetAdapter method is used to return a new Instance of the OutlookBar by using a OutlookBarUIAdapter.
The Supports method is used to check if a uiElement need such type of an adapter (the object builder will use this method to find out how to build a specific uiElement)

As you could have seen the code above uses the OutlookBarUIAdapter to create the new OutlookBar instance, lets code the OutlookBarUIAdapter:

    public class OutlookBarUIAdapter : UIElementAdapter<OutlookBarButton>
    {
        private OutlookBar outlookBar;
        public OutlookBarUIAdapter(OutlookBar outlookBar)
        {
            Guard.ArgumentNotNull(outlookBar,"outlookBar");
            this.outlookBar = outlookBar;
        }
        protected override OutlookBarButton Add(OutlookBarButton uiElement)
        {
            if (outlookBar == null)
                throw new InvalidOperationException();

            outlookBar.Buttons.Add(uiElement);
            return uiElement;
        }
        protected override void Remove(OutlookBarButton uiElement)
        {
            if (outlookBar == null)
                throw new InvalidOperationException();
            outlookBar.Buttons.Remove(uiElement);
        }
    }

The constructor get an OutlookBar instance and save reference for it localy in order to add / remove items (or just call it buttons in this case) from the bar.

The Add and Remove implements the specific implementation of the UI component of adding uiElements to it I.e. how to add/remove buttons from the OutlookBar component.

After we've created the adapters we still need to introduce them to the CAB infrastructure and we do this by adding the specific adapter to the IUIElementAdapterFactoryCatalog, this way the Object Builder will be able to find the missing adapter for the OutlookBar.

Add the following code to the AfterShellCreated of your shell application class or at the load event of your layout module.

IUIElementAdapterFactoryCatalog catalog = RootWorkItem.Services.Get<IUIElementAdapterFactoryCatalog>();
catalog.RegisterFactory(new OutlookBarAdapterFactory());

At last we can now use our new OutlookBar UIExtension through our modules, add the following to the run method of your module:

OutlookBarButton btnShowCalendar = new OutlookBarButton();
btnShowCalendar.Text = "New Module Button";
WorkItem.UIExtensionSites[Interface.Constants.UIExtensionSiteNames.MainOutlookBar].Add
                <OutlookStyleControls.OutlookBarButton>(btnShowCalendar);

We've added a new button through the OutlookBar UIExtension site but we are facing two problems here:

1. We still need to see how we can add also the ability to add command invokers to the buttons - we will cover this on the next CAB post.

2. We have just created a coupling as the module needs to know what is an OutlookBarButton - I will show you how to remove this coupling using a simple design also in one of the next post of out series.

1 Comment

  • Yeah, uh, even after following your instructions here, I still could not get the initial error that you display an image of in your article to go away.

Comments have been disabled for this content.