The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








I heart FeedBurner

Latest Diggs - Programming

Computers Blogs - Blog Top Sites

Site Hits

Total: 4,896,068
since: 19 Jan 2005

Extending your Application with MAF/VSTA AddIns - Part II

posted Thu 20 Jul 06
This is a continuation of this blog post.

The first thing we need is an application, and we need some goals. We need a list of things that we hope to accomplish with this addin framework. For me, being nearly as new to it as everyone else, I don't know if all of the goals that I have are going to be possible..but, we never learn unless we try things that are hard, right? So, here's what I want to accomplish with this AddIn-Aware application:
  • 3rd party Addin should be able to listen to, and respond to, events thrown by the host application.
    • In response to said events, the addin should be able to create its own GUI and display it to the user, either by creating a new Form instance or by using the MessageBox.Show, etc.
  • 3rd party Addin should be able to affect the host application's GUI. I don't know necesarily how this can be accomplished, but I want the GUI of the host application to change. This could be done by possibly having the addin create an instance of a custom control, and then add that control to a parent container/panel within the host GUI.
  • 3rd party addin should have contextual injections. So, when I right-click something in the host menu, I should be able to poll the third party addins to see if any of them want to contribute line items to a context menu. This type of functionality should be available wherever the host application wants it.
At this point, I don't know if this is too ambitious or not, so let's just get started with a simple Windows Forms application. The one I created is called AddInHostDemo. Basically I've got my empty solution open on the right side and I've got the SDK Sample ShapeAppCSharp open on the left side, and I'm following the walkthrough guidelines from the SDK itself. First thing I notice throws me for a loop. Basically, you have to try and remember back to the good old days of Office Automation when you used to have a Word.Application object or an Excel.Application object. From the Application object, the automation host then exposed everything that you needed in order to integrate with the rest of the application. This is exactly how we're going to do this here.

One problem: Whoever wrote this sample has never read the design guidelines or naming conventions for the .NET Framework. The person who created the shape app sample named this root object Application. Of course, there is already a System.Windows.Forms.Application object. Rather than resolving the naming conflict by giving the custom class a meaningful name, this sample just starts using full namespace references. This is where my code and the sample are going to start divering. A LOT. So, add a class to your project called HostedApplicationRoot. This is a meaningful name in that it conveys that its true purpose is to act as the root of an exposed object model, hosted within your application. Make sure you rename your default Windows Forms class to MainForm as well.

The code for this class should be as follows:

public partial class HostedApplicationRoot : System.Windows.Forms.IWin32Window
{
  private MainForm _form; // MainForm is the main form class

  internal HostedApplicationRoot()
  {
    _form = new MainForm(this); // modified constructor
  }

  internal MainForm ApplicationForm
  {
    get { return _form; }
    set { _form = value; }
  }

  public IntPtr Handle
  {
    get { return (IntPtr)_form.Handle;
  }

  public EventHandler<EventArgs> ApplicationStarted;

}


Now, I know that I said I was going to create a "normal" application and then upgrade it to deal with plug-ins. Well, I'm cheating a little bit here because I want the hosted application root object to be manageable, in a place where I can reach it, and stored in such a scope as that it won't be accidentally garbage collected. I still haven't done any real plug-in work, I've just created something that looks a little out from the norm. I've also modified the constructor of my main form to look like this:

private HostedApplicationRoot _application;

internal MainForm(HostedApplicationRoot application)
{
    InitializeComponent();
    _application = application;

    EventHelper.Invoke( _application.ApplicationStarted, null);
}


This basically uses the HostedApplicationRoot application to fire off the OnApplicationStarted event. In theory, a plugin/addin might be able to respond to this event..which would be so cool. The only thing left to do is to tweak the part of Program.cs that creates a new instance of the main form class and runs it. What we want that code to do instead is to run the instance of the main form class contained within the HostedApplicationRoot class. Using this model, I should have complete control over what functionality is exposed, how its exposed, and what can and cannot be done to my object model from remote code. Here is the modified Program.cs (I've left off the namespace using statements to keep things simple and clear, you get the idea):

static class Program
{
    static private HostedApplicationRoot _application;

    static public HostedApplicationRoot HostedApplication
    {
        get { return _application; }
    }

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        _application = new HostedApplicationRoot();
        Application.Run( _application.MainForm );
    }
}


If you've gotten this far without falling asleep, congratulations. At this point, you might also be wondering - why did he make the HostedApplicationRoot class partial. Well, there's a really good reason for it. And again, its kind of a cheat since I know what's coming. When proxies are generated for this application, they are going to make use of the partial keyword to extend the class itself. So, thats why I'm using the partial keyword.

In the next article, I am going to add some more tweaks to the HostedApplicationRoot so I can see what kind of functionality gets exposed. Then I will create the AddIn proxies using proxygen.exe, and I will create Visual Studio project templates using projectgen.exe. If that all works out, I'll be ready to create my first AddIn.

Note that I have to register this application as an AddIn host before I can create the Visual Studio Project Templates, so that step will also be done in the next article.

tags:            

links: digg this    del.icio.us    technorati    reddit

AddThis Social Bookmark Button




1. Gary left...

Kevin, you wrote: "The person who created the shape app sample named this root object Application. Of course, there is already a System.Windows.Forms.Application object. Rather than resolving the naming conflict by giving the custom class a meaningful name, this sample just starts using full namespace references."

I think that one justification for using 'Application' is for consistency with existing, VBA Object models, that almost universally used the 'Application' naming convention. You alluded to this in your previous paragraph:

"...good old days of Office Automation when you used to have a Word.Application object or an Excel.Application object"

In further defense, the ShapeApp samples are all consistently named this way and only the managed (C#) samples have this reserved word naming conflict.

Your point is valid anyway, because a more appropriate way to expose an 'Application' object in the proxy, would be to use the 'newName' attribute in the XML descriptor file used by proxygen. for example:

  • <Class originalFullyQualifiedName="AddInHostDemo.HostedApplicationRoot" newName="Application" isExcluded="false" newNamespace="AddInHostDemo." visibility="public" isStatic="false" isAddInEntryPoint="true" />

Then the Host application could assign any suitable class (with any name) as its root object in the proxy, and rename for Addin programmers


2. Kevin Hoffman left...
Fri 11 Aug 06 1:22 pm

I couldn't agree with you more. I know that the reason the 'Application' is used is to be consistent with VBA apps.

That was my PROBLEM. This is NOT VBA. This has no relation to VBA, we're talking managed .NET code here - the less we have to think about VBA and COM the better.


Tag Related Posts

WPF Bumper Stickers

Tue 12 Dec 06 7:32 P GMT-05

First Impressions of Windows Vista RC1

Thu 07 Sep 06 1:30 P GMT-05
tags:                      

Localizing a WPF Application

Tue 22 Aug 06 11:39 A GMT-05
tags:            

WPF Slide Show and Photo Album

Fri 18 Aug 06 6:48 P GMT-05

Is Windows Workflow Foundation Too Complex?

Fri 18 Aug 06 12:15 P GMT-05

ADO.NET Entity Framework Announced Today!

Wed 16 Aug 06 11:08 A GMT-05

Extending your Applications with MAF/VSTA - Part III

Fri 21 Jul 06 12:30 A GMT-05
tags:            

Extending your Application with MAF/VSTA AddIns

Thu 20 Jul 06 1:51 A GMT-05
tags:            

July CTP is out... yay.

Tue 18 Jul 06 6:34 P GMT-05
tags:    

.NET Framework 3.0 June CTP is out!

Fri 23 Jun 06 6:23 P GMT-05
tags: