MEF in Office?

In my last post, I looked briefly at MEF, and I’m wondering how this model can be applied to Office add-ins. The Office add-in model itself already achieves a level of dynamic composition, by virtue of the fact that the set of add-ins to be loaded is only discovered at runtime. However, even though the model is similar at a high level, Office itself does not use MEF.

Nonethless, MEF could perhaps be useful in an Office context. For example, suppose you want to build the add-in itself as a composite. That is, suppose your total add-in functionality is made up of components – and furthermore, you want to discover these components at runtime, instead of statically linking them at design-time.

In this post, I’ll translate the simple console MEF app from my previous post to an Office add-in.

This turned out to be remarkably easy. I used the same ‘Interface’ assembly, the same ‘Implementation’ assembly, and the same ‘Alternate’ implementation assembly. The only difference was that I built a VSTO add-in instead of a console app. This is the code in my add-in class:

[Import]

public Interface.ICalculate Calculate { get; set; }

private void ThisAddIn_Startup(object sender, System.EventArgs e)

{

    DirectoryCatalog catalog =

        new DirectoryCatalog(

            AppDomain.CurrentDomain.BaseDirectory);

    CompositionContainer container =

        new CompositionContainer(catalog);

    CompositionBatch batch = new CompositionBatch();

    batch.AddPart(this);

    container.Compose(batch);

}

internal void GetCircumference()

{

    double d = (double)this.Application.ActiveCell.Value2;

    this.Application.ActiveCell.get_Offset(0, 1).Value2 =

        Calculate.Circumference(d);

}

As you can see, the code is almost identical to what I had in my console app. The difference here is that I split up the composition from the method invocation. I set up the catalog and CompositionContainer and perform the composition on startup, but I defer actually invoking the imported method until the user clicks my custom Ribbon button:

public partial class RibbonX : OfficeRibbon

{

    public RibbonX()

    {

        InitializeComponent();

    }

    private void RibbonX_Load(object sender, RibbonUIEventArgs e)

    {

    }

    private void buttonCircumference_Click(

        object sender, RibbonControlEventArgs e)

    {

        Globals.ThisAddIn.GetCircumference();

    }

}

As before, this works with either my first implementation of the contract, or the alternate version.

One obvious question that springs to mind is, “can this model be used to mitigate the versioning problem in Office?”. Specifically, for example, could you use this model to build an add-in that dynamically composes components that implement either 2003-style CommandBar UI or 2007-style Ribbon/TaskPane UI? The answer is, No, probably not in any useful way. If you think about it, at the core of the MEF model is the contract: the model relies on the contract being the ‘fixed’ point in the moving constellation of composable parts. In the CommandBar vs Ribbon scenario, the contract is not fixed – you’re either using CommandBar interfaces or you’re using Ribbon/TaskPane interfaces.

You can perhaps envisage an imaginary IOfficeUi interface, which one component might implement to render commandbars while another component implements the same interface to render ribbons – but this is an imaginary scenario, and it does not map to the reality of Office versioning. Where the Office add-in scenario does map to the MEF model is in the composability of add-ins – because all add-ins must implement IDTExtensibility2, and they can implement it in arbitrarily different ways (within some constraints). However, back to what I said at the top of the post – this is conceptually similar to the MEF model, but does not use MEF.

All right, then, you’re thinking – why can’t we devise such an IOfficeUi interface for use between our add-in and composable parts that implement this differently? Well, of course, we could do this – but not in any useful way. Imagine what you’d need to build… Because of the way Office uses the new optional extensibility interfaces (IRibbonExtensibility, ICustomTaskPaneConsumer, etc), your add-in must advertise that it either does or does not implement these interfaces very early on in its lifetime – very soon after it is loaded. If you don’t tell Office up front that you implement these interfaces, you’ll never get called back on them. So, even if you build an Office 2003 add-in (which does not use these interfaces), you’d still be obliged to implement them in case you’re actually running in Office 2007 or later.

Moreover, the imaginary IOfficeUi interface would probably end up being very generic – plus you’d have the problem of how to communicate between the UI and the business logic in your solution. You’d end up with some hopelessly generic interfaces like this:

public interface IOfficeUi

{

    // In Initialize, construct CommandBars or Ribbon/TaskPane.
    void Initialize(object addIn);

    IBusinessLogic BusinessLogic { get; set; }

}

public interface IBusinessLogic

{

    // In InvokeMethod, invoke business logic from the UI event handlers.
    object InvokeMethod(string methodID, params object[] args);

    IOfficeUi OfficeUi { get; set; }

}

This is not really a workable model. So, for the UI versioning case, the only really workable approach is to componentize along the lines of my post here, using the lowest-common denominator but implementing later interfaces. MEF doesn’t help you in this scenario.

 

MefVstoAddIn.zip