Hosting CAB in a UserControl - In depth

In the previous post,
I've described an approach to host a CAB application in Outlook, Word,
or any other .net-compatible application. In this post, I will analyze
the internals of CAB as well as the code I had to write to implement
that.

In a normal CAB application (FormShellApplication) the
Run method initialize the whole CAB infrastructure, the root work item and the
modules.


Every CAB Application inherits from the CabApplication
class. This is the “heart” of CAB, where all the initialization takes place.

The Run method of CabApplication could
be separated in three stages:

hcab1

  1. These lines of code will add the ObjectBuilder builder
    strategies, the required services, will build up and initialize the root
    workitem and finally it will run it
  2. The second part will execute the Start method. This method
    is abstract and must be overridden in derived applications. The FormShellApplication
    will override it in order to start the Winforms application using the
    Shell as the main form

    protected override void Start()

    {

        Application.Run(Shell);

    }

    This line of code will start
    the message pump and the winforms application will start listening UI messages
    until the shell is closed. When that happens, part 3 is executed

  3. The disposal of the root workitem and any visualizer
    configured happens here.

What happens when you want to host CAB in a UserControl
and not in a regular Form?

The problem is that the message pump has already been
created by the host application: either a Winforms (not designed with CAB), an
Outlook addin, a Word Document, etc. That means that the Start method
cannot execute Application.Run(Shell). However, if we don’t execute Application.Run,
the thread will keep executing and will dispose the workitem (part 3)

How this could be solved?

We need to delay the disposal of the root work item until we
decide it is ok to dipose it. To achieve this, a new workitem need to be
created. This root workitem will override the base Dispose(bool disposing)
method in order to have control over when the Dispose needs to be executed.

public class UserControlWorkItem
: WorkItem

{

    private bool
_dispose = false;

public

void
DoDispose()

{

_dispose = true;

Dispose(true);

GC

.SuppressFinalize(this);

}

    protected override
void Dispose(bool
disposing) {

        if (_dispose) {

            base.Dispose(disposing);

}

}

}

Then, the UserControlShellApplication will be defined
with the following code. Note the TWorkItem being of type UserControlWorkItem
and the TUserControlShell of type UserControl.


public class UserControlShellApplication : WindowsFormsApplication

    where TWorkItem : UserControlWorkItem,
new()

    where TUserControlShell : UserControl, new()

{

    protected override
void Start()

{

}

public

void
Dispose() {

RootWorkItem.DoDispose();

}

    public TUserControlShell UserControlShell {

        get { return base.Shell; }

}

}

Finally, we need to host the CAB application in a
UserControl. This works just as a container and it is different from the
UserControl that will act as a Shell. The UserControl will host an instance of UserControlShellApplication
and will execute the Run method. Also, when the UserControlHost
is disposed it will also dispose the UserControlShellApplication that
will finally dispose the UserControlWorkItem.

public partial class UserControlHost : UserControl

where

TUserControlShellApplication : UserControlShellApplication,
new()

    where TUserControlShell : UserControl, new()

{

protected

virtual void Run()

{

_cabApp = new TUserControlShellApplication();

_cabApp.Run();

_cabApp.UserControlShell.Dock = DockStyle.Fill;

        this.Controls.Add(_cabApp.UserControlShell);

OnStarted(EventArgs.Empty);

}

    protected override void
Dispose(bool disposing)

{

if

(disposing) {

_cabApp.Dispose();

}

        base.Dispose(disposing);

}

…

}

Note: as shown in part 3 the Visualizer is also
disposed. The same approach applies to the visualizer.

Published: February 26 2006

blog comments powered by Disqus