Hosting Windows Forms controls in Office 2007 Custom Task Pane's is pretty simple, as shown in this MSDN article. I created a simple addin to display a list of image files in a given folder so that the user can double-click on the file name (in a ListBox) and insert the picture into the document. With little effort it was simple to instead implement a WPF UserControl to spruce up the addin. You can download the code here.
You can host WPF Elements using the ElementHost control found in the WindowsFormsIntegration assemly that ships with the .NET 3.0 runtime. The CustomTaskPane requires a Windows Forms UserControl, so I'm just adding the ElementHost control to a plain Windows Forms UserControl and then passing that UserControl to be created with the CustomTaskPane.
internal void AddTaskPane()
{
ElementHost host = new ElementHost();
browser = new ImageBrowserControl();
host.Child = browser;
host.Dock = DockStyle.Fill;
browser.PictureSelected += ImageBrowserControl_PictureSelected;
UserControl userControl = new UserControl();
userControl.Controls.Add(host);
pane = this.CustomTaskPanes.Add(userControl, "Image Browser");
pane.Visible = true;
}
Inserting images into a Word document is just as simple, as shown in the following code snippet:
private void ImageBrowserControl_PictureSelected(Object sender, EventArgs<Picture> e)
{
Object missing = System.Reflection.Missing.Value;
Globals.ThisAddIn.Application.Selection.InlineShapes.AddPicture(
e.Item.Filename, ref missing, ref missing, ref missing);
}
The EventArgs<T> class used in the previous handler is a nice trick to use in conjunction with EventHandler<T>. A tip from Jean-Paul S. Boodhoo's Blog.
public class EventArgs<T> : EventArgs
{
private T m_Item;
public EventArgs(T item)
{
m_Item = item;
}
public T Item
{
get
{
return m_Item;
}
}
}
public event EventHandler<EventArgs<Picture>> PictureSelected;
To create a Ribbon tab you define a set of XML. The onAction on the button is an event that you can handle in your codebehind file.
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoad">
<ribbon>
<tabs>
<tab id="VSTOAddIns" label="Addins" >
<group id="Group1" label="Helpers" visible ="1">
<button id="ImageBrowser" label="Insert Image"
onAction="ImageButtonClick"
imageMso="ContentControlDate"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Handler for toggling showing / hiding the task pane:
public void ImageButtonClick(Office.IRibbonControl control)
{
if (!m_ImagePaneExists)
{
Globals.ThisAddIn.AddTaskPane();
}
else
{
Globals.ThisAddIn.RemoveTaskPane();
}
m_ImagePaneExists = !m_ImagePaneExists;
}
The WPF UserControl styles a simple Picture class, shown below:
public class Picture
{
private FileInfo m_FileInfo;
public Picture(String filename)
{
m_FileInfo = new FileInfo(filename);
}
public String Filename
{
get { return m_FileInfo.FullName; }
set { m_FileInfo = new FileInfo(value); }
}
public String Name
{
get { return m_FileInfo.Name; }
}
}
DataTemplate for the Picture class:
<DataTemplate DataType="{x:Type local:Picture}">
<StackPanel MinHeight="0px" MinWidth="0px" MaxHeight="150px" MaxWidth="150px" >
<Image Stretch="UniformToFill" Source="{Binding Filename}" />
<TextBlock Text="{Binding Name}"
FontWeight="Bold"
HorizontalAlignment="Center"
/>
</StackPanel>
</DataTemplate>
The ListBox style is taken from my previous blog post on styling ListBoxItem's.
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="ItemBorder"
BorderBrush="Black"
Background="LightGray"
BorderThickness="2"
CornerRadius="4"
Margin="3"
>
<ContentPresenter Margin="2" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ItemBorder"
Property="BorderBrush"
Value="Red"
/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ItemBorder"
Property="BorderBrush"
Value="Blue"
/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False" />
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="ItemBorder"
Property="Opacity"
Value="0.50"
/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
A couple things to remember when building Word Addins is that all Assembly's that you use need to be signed and granted full-trust by the .NET runtime for Word to load them. Other than that creating Word Addins is pretty simple!