Using Windows Presentation Foundation in VSTO -- with "Cross-bow"
I've been working on this on and off since TechEd. I had a customer ask if it was possible to use WPF (formerly Avalon) in a VSTO Custom Task Pane. It is possible, and we're dealing with CTP (Community Technical Preview) releases on top of beta releases, so considering that, it works fairly well. I told the customer I'd write up a quick little sample to show him how it is done, and I decided to share that sample here as well.
It didn't turn out to be as quick as I'd hoped, but at long last it is done. As with any sample code, it is provided "as is" with no warranties or promises implied or stated. It is just to show you how to get it going.
To use this sample, the first thing you need to do is get everything installed. You need Visual Studio Tools for Office 2005 (or one of the Visual Studio Enterprise Editions that contains the VSTO projects) to get started. Then you need to start installing the betas and CTPs. I installed all of this stuff onto a Virtual PC. I definitely don't recommend just installing this beta stuff on your main machine. VPC is a convienient way to go.
The first beta you need is the Office 2007 Beta 2. You can get that here.
Next you need the .NET Framework 3.0 June CTP. You may recall that we recently reverted the name of the new .NET Framework pieces from WinFX back to the .NET Framework. There is still a download for WinFX 3.0 Runtime Components Beta 2 out there. In fact, this is what I was using up until today when I started writing this up. One of the things that took this sample so long to get done was that I was trying to workaround some UI painting bugs in Beta 2. Then I noticed that there was an updated the drop (and that we changed the name). The good news is that these painting bugs have been fixed. So I have updated my sample to use this new CTP. If you still have beta 2 of WinFX, then the code will likely compile fine, but you will probably have trouble getting the WPF control to show up. Resizing the Custom Task Pane got it to show up. But as I said, with the latest drop, this problem is fixed. You can download the .NET Framework 3.0 June CTP here. If you are updating from Beta 2 be sure to read the release notes about removing Beta 2 and installing the CTP.
Next you need the June CTP of VSTO "v3." This is available here, but first you must perform the workaround I described in my previous post (here) that will allow it to install over the .NET Framework 3.0.
And finally you need the June CTP of the .NET Framework 3.0 Development Tools (codename "Cider"). To use these you will need the June CTP of the .NET Framework 3.0. The older drop that worked with WinFX Beta 2 seems to have been removed – I couldn’t find it anyway.
So now your machine is set up, let’s look at the solution I provided. You’ll notice there are six projects. ExcelAddin1 and WordAddin1 are what you’d expect them to be, addins for Excel and Word respectively. We’ll get to those in a minute. There are also the associated setup projects for those addins. You can ignore these – they come along with the addin projects and would be used if you wanted to publish your addins.
Finally there are two WPF projects: TaskPaneControlDesign and TaskPaneControls. The first – TaskPaneControlDesign – I created as a convenience. You’ll see why when you look at the TaskPaneControls.UserControl1.xaml. When you double-click on this, it brings up the “designer” and as you can see the designer for user controls has not been implemented yet.
So I created the TaskPaneControlDesign project simply so I could use the designer to layout my control. Open up Window1.xaml and you’ll see my form. I then went into the XAML view and cut and pasted the xaml from the <grid> tag on down into the WPFUserControl.xaml. As you can see, WPFUserControl is very simple: it has a TextBox and a Button. When you click the button it throws up a message box showing whatever is in the TextBox. Just something quick and easy to get this going.
The "cross-bow" integration code is then duplicated in each of the addin projects. It is the same, so this applies to both. The first step is to make the proper references. You’ll need to add references to the WPF assemblies: PresentationCore, PresentationFramework and WindowsBase. You’ll also need to reference the “cross-bow” assembly: WindowsFormsIntegration. “Cross-bow” contains the ElementHost class that is used to bridge between WinForms and WPF.
You’ll note that I have my WPF control in a separate project from the Addin. I did this on purpose. I believe it is possible to mingle WinForms and WPF code in the same project, but you get into a bunch of name collisions between the different namespaces, since both WinForms and WPF have a Button class, among many others. I found it much easier to keep the two worlds separate. So, next you’ll need to reference your WPF project so you can see the control.
Then you'll need to add a WinForms UserControl to the addin project. You will use this as the basis for the Custom Task Pane. Go into the code view of the UserControl in the sample and you will see this code:
private System.Windows.Forms.Integration.ElementHost elemHost;
this.elemHost = new System.Windows.Forms.Integration.ElementHost();
this.elemHost.Child = new TaskPaneControls.WPFUserControl();
this.elemHost.Dock = DockStyle.Fill;
So it is really very simple. ElementHost is a WinForms based control that can host WPF Controls. We create an instance of ElementHost, then create an instance of our WPF Control (TaskPaneControls.WPFUserControl) and set it as the Child on the ElementHost. I then set the DockStyle to DockStyle.Fill on ElementHost to fill up the WinForms UserControl. Then I add the elementHost to the Controls collection on the WinForms UserControl.
The final piece of code is in ThisApplication.cs, and is the standard code for adding a CustomTaskPane to Word or Excel:
private void ThisApplication_Startup(object sender, System.EventArgs e)
Microsoft.Office.Tools.CustomTaskPane ctp =
new WinFormsUserControl(), "WPF in Crossbow");
ctp.Visible = true;
Of course in this code, we add the WinFormsUserControl to the Custom Task Pane.
Hit F5 and everything should work. I’ve set the WordAddin as the start up project because it works better. There is a bug with focus somewhere that affects the ExcelAddin – it seems impossible to actually type into the text box in the WPF control.
But this is what Betas and CTPs are for, after all, finding bugs. Please try this stuff out and report bugs as you find them. Try out the other Office applications, use a bunch of controls, make them do fun things. The more we bang on it now, the better it will be when we ship.