Introduction to UIExtensionSites (Introduction to the CAB/SCSF Part 13)

Introduction

Part 12 of this series of articles went into some detail on events in the Composite Application Block, and concluded our discussions of commands and events.

As we’ve already discussed in this series of articles, the Composite Application Block (CAB) is intended as a means of creating smart client applications that are composed of several independent ‘modules’. We’ve also seen that the CAB has a number of other features to make development easier in general, in particular a dependency injection framework.

However, when we think of a ‘smart client’ we usually think of a well-designed and powerful user interface. The CAB introduces a number of new classes to aid in the design and implementation of such user interfaces.

So far this series of articles has not really discussed user interfaces at all, and certainly hasn’t covered any of the user interface elements of the CAB. The next few articles will remedy this.

UIExtensionSites

This article and the next will discuss one of the CAB user interface classes, the UIExtensionSite. A UIExtensionSite is a user interface element of the shell (the containing window) for a composite application. It might be a ToolStrip, a MenuStrip or a StatusBar, but could be any shared user interface element. The idea of the UIExtensionSite class is to allow each individual module to talk to these elements of the shell in a decoupled way.

In this article we’ll start with a basic example. In fact, we’re going to base the example on an earlier one, which will be recapped first.

Basic Example Starting Point – Recap of the BasicMDIApplication from Part 5

For this example we start with the code for the first example from part 5 of this series of articles, ‘BasicMDIApplication’. This contains three projects, none of which reference each other. However, each project contains one form, and when we run the application two of the forms appear as MDI children of the third. The two MDI children are coloured red and blue respectively.

This was done in a slightly contrived way (it’s not the way you’d do it in production code). In our Shell project in method AfterShellCreated we added the Shell form itself to our RootWorkItem’s Items collection:

        protected override void AfterShellCreated()
        {
            base.AfterShellCreated();
            this.Shell.IsMdiContainer = true;
            RootWorkItem.Items.Add(this.Shell, "Shell");
        }

Then we accessed it from the same collection in each of the other projects. We did this in the Load method of theModuleInit class in each project. There we set the MdiParent property of the project’s form to be the Shell form:

        public override void Load()
        {
            base.Load();
            Form shell = (Form)parentWorkItem.Items["Shell"];
            Form1 form = new Form1();
            form.MdiParent = shell;
            form.Show();
        }

Note that here we had access to the parentWorkItem of the project (which is the same as the RootWorkItem). We got access to this through dependency injection.

The original code is available here.

Basic Example Extended

We’ll extend this so that the Shell form has a ToolStrip with two buttons. One of these will bring the red MDI child to the front, and one will bring the blue MDI child to the front. Remember that there are no direct references between any of the projects, so it isn’t immediately obvious how we do this.

Furthermore, each individual project will set up its own ToolStrip button. So the ToolStrip itself will be in the Shell project. The Red project will contain code to create the ‘Show Red Form’ button and add it to the ToolStrip. Similarly the Blue project will contain code to create the ‘ShowBlueForm’ button and add it to the ToolStrip.

Basic Example – Code in the Shell Project

To do this we first add the ToolStrip to the Shell form using the visual designer. We’ll call it ‘shellToolStrip’. To make life easy in this simple example we’ll give it a scope of ‘Internal’ (by changing the ‘Modifiers’ property).

Then we extend AfterShellCreated to register this ToolStrip as a UIExtensionSite:

        protected override void AfterShellCreated()
        {
            base.AfterShellCreated();
            this.Shell.IsMdiContainer = true;
            RootWorkItem.Items.Add(this.Shell, "Shell");
            RootWorkItem.UIExtensionSites.RegisterSite("ShellToolStrip", this.Shell.shellToolStrip);
        }

The RegisterSite method has the effect of adding the object passed in its second parameter (the shellToolStrip) to the UIExtensionSites collection of the RootWorkItem. The UIExtensionSites collection is indexed by name, and RegisterSite gives the object the name passed in the first parameter (“ShellToolStrip”).

Basic Example – Code in the MDI Child Projects

As in the original example, we have access to the RootWorkItem in the individual projects containing the MDI child forms. So we can retrieve a UIExtensionSite object from the UIExtensionSites collection of that RootWorkItem. The UIExtensionSite object represents the ToolStrip. UIExtensionSite has an Add method we can use to add a ToolStripButton to the UIExtensionSite.

So in the ModuleInit classes (RedModuleInit and BlueModuleInit) of the appropriate MDI child projects we create such a button. We give it an appropriate event handler for its Click event. We’re using standard .NET events here:

        private Form1 form;
        public override void Load()
        {
            // Code as before
            base.Load();
            Form shell = (Form)parentWorkItem.Items["Shell"];
            form = new Form1();
            form.MdiParent = shell;
            form.Show();

            // Create a new button, hook it up to an event handler, 
            // and add it to our ToolStrip by using the UIExtensionSite.
            ToolStripButton toolStripButton = new ToolStripButton("Show Red Screen");
            toolStripButton.Click += new System.EventHandler(toolStripButton_Click);
            UIExtensionSite uiExtensionSite = parentWorkItem.UIExtensionSites["ShellToolStrip"];
            uiExtensionSite.Add<ToolStripButton>(toolStripButton);
        }

        void toolStripButton_Click(object sender, System.EventArgs e)
        {
            form.BringToFront();
        }

That’s all there is to this example. The only change to the above code for the Blue project is that the ToolStripButton is initialized to have Text “Show Blue Button” rather than “Show Red Button”.

Basic Example – Results

The code for this is available. If we run it we get a screen as below. Clicking “Show Blue Screen” brings the blue child form to the front, clicking “Show Red Screen” brings the red child form to the front.

ToolStripMDIApplication

As previously with this example, please note that this is not the way you would normally set up an MDI application in the CAB (although it might well be the way you would set up a ToolStrip). The example simplifies many aspects of the code in order to demonstrate the core concepts.

Conclusion

In this article we’ve created an application with three separate projects that don’t reference each other, but having a common ToolStrip in the Shell project with buttons contributed by the other projects.

In part 14 we will examine how this is working in more detail, show how we can use UIExtensionSites for other user interface elements, and discuss what value this is adding to the development process.

4 thoughts on “Introduction to UIExtensionSites (Introduction to the CAB/SCSF Part 13)

  1. In terms of decoupling modules, one must use
    1. ProfileCatalog.xml (allow CAB to know which modules to be loaded)
    2. [ServiceDependency] (allow CAB for doing “dependency injection”)
    3. Hard-code “ShellToolStrip” (allow modules to know how to get shell toolstrip)

    In my opinion, 1 and 3 are OK. However, using attributes (2) does not give very clear picture.
    We had seen so many macros in MFC in old days.

  2. Greetings I recently finished going through through your blog and I’m very impressed. I actually do have a couple inquiries for you personally however. You think you’re thinking about doing a follow-up posting about this? Will you be likely to keep bringing up-to-date at the same time?

Leave a comment