Pieter van der Westhuizen

Windows Presentation Foundation, Office 2010 and Add-in Express 2010

Windows Presentation Foundation (WPF) was first introduced with .NET Framework 3.0 and gives developers and designers a new system for rendering intuitive user interfaces.

As with most users, Northwind Traders saw the impressive UI’s being designed using WPF and thus requested that certain elements of their system should use WPF.  In this post I’ll show you an easy way to use WPF in your own MS Office Add-ins.

First, start by creating a new COM Add-in:

Creating a new Add-in Express COM Add-in

This Add-in will be for MS Office Outlook so go ahead and finish the wizard. We’ll add a task pane to Outlook to show whether a customer has reached their credit limit. Once the wizard is completed, add a new user control to your project:

Adding a new user control to the project

Next, add a new WPF User Control to your project:

Adding a new WPF User Control to the project

We’ll also add two custom WPF buttons to the WPF User Control and a text block. Open the WPF User Control and view the XAML code. Add the following XAML code above the Grid element.

<UserControl.Resources>
    <RadialGradientBrush x:Key="Orange_BACKGROUND"
                         GradientOrigin="0.5,0.5">
        <RadialGradientBrush.RelativeTransform>
            <TransformGroup>
                <ScaleTransform CenterX="0.5"
                                CenterY="0.5"
                                ScaleX="0.865"
                                ScaleY="0.805"/>
                <SkewTransform AngleX="0"
                               AngleY="0"
                               CenterX="0.5"
                               CenterY="0.5"/>
                <RotateTransform Angle="135.194"
                                 CenterX="0.5"
                                 CenterY="0.5"/>
                <TranslateTransform X="0.006" Y="0.124"/>
            </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Color="#FFDA6000" Offset="1"/>
        <GradientStop Color="#FFF9FF00" Offset="0"/>
    </RadialGradientBrush>
    <Style x:Key="ButtonStyle1" TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup
                                x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames
                                            BeginTime="00:00:00"
                                            Storyboard.TargetName="ellipse1"
                                            Storyboard.TargetProperty=
                                            "(UIElement.Opacity)">
 
                                            <EasingDoubleKeyFrame
                                                KeyTime="00:00:00.2000000"
                                                Value="0.1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames
                                            BeginTime="00:00:00"
                                            Storyboard.TargetName="ellipse"
                                            Storyboard.TargetProperty=
                                            "(Shape.StrokeThickness)">
 
                                            <EasingDoubleKeyFrame
                                                KeyTime="00:00:00.1000000"
                                                Value="6"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames
                                            BeginTime="00:00:00"
                                            Storyboard.TargetName="ellipse1"
                                            Storyboard.TargetProperty=
                                            "(FrameworkElement.Margin)">
 
                                            <DiscreteObjectKeyFrame
                                                KeyTime="00:00:00.1000000">
 
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Thickness>2,2,3,4
                                                    </Thickness>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames
                                            BeginTime="00:00:00"
                                            Storyboard.TargetName="ellipse1"
                                            Storyboard.TargetProperty=
                                            "(UIElement.Opacity)">
 
                                            <EasingDoubleKeyFrame
                                                KeyTime="00:00:00.1000000"
                                                Value="0.1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames
                                            BeginTime="00:00:00"
                                            Duration="00:00:00.0010000"
                                            Storyboard.TargetName="ellipse1"
                                            Storyboard.TargetProperty=
                                            "(UIElement.Opacity)">
 
                                            <EasingDoubleKeyFrame
                                                KeyTime="00:00:00"
                                                Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
 
                                        <DoubleAnimationUsingKeyFrames
                                            BeginTime="00:00:00"
                                            Storyboard.TargetName="ellipse"
                                            Storyboard.TargetProperty=
                                            "(Shape.StrokeThickness)">
 
                                            <EasingDoubleKeyFrame
                                                KeyTime="00:00:00.1000000"
                                                Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
 
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="FocusStates">
                                <VisualState x:Name="Focused"/>
                                <VisualState x:Name="Unfocused"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="0.08*"/>
                            <RowDefinition Height="0.84*"/>
                            <RowDefinition Height="0.08*"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="0.08*"/>
                            <ColumnDefinition Width="0.84*"/>
                            <ColumnDefinition Width="0.08*"/>
                        </Grid.ColumnDefinitions>
                        <Ellipse Grid.ColumnSpan="3"
                                 Grid.RowSpan="3"
                                 Margin="1,1,1,1"
                                 Stroke="#FF8C8C8C">
 
                            <Ellipse.Fill>
                                <LinearGradientBrush
                                    EndPoint="0.5,1"
                                    StartPoint="0.5,0">
 
                                    <GradientStop
                                        Color="#FF343434"
                                        Offset="0.63"/>
                                    <GradientStop
                                        Color="#FFFFFFFF"
                                        Offset="0.158"/>
                                    <GradientStop
                                        Color="#FFB3B3B3"
                                        Offset="1"/>
                                    <GradientStop
                                        Color="#FFE0E0E0"
                                        Offset="0"/>
                                </LinearGradientBrush>
                            </Ellipse.Fill>
                        </Ellipse>
                        <Grid Grid.Column="1"
                              Grid.ColumnSpan="1"
                              Grid.Row="1"
                              Grid.RowSpan="1">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="0.033*"/>
                                <RowDefinition Height="0.6*"/>
                                <RowDefinition Height="0.367*"/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.114*"/>
                                <ColumnDefinition Width="0.772*"/>
                                <ColumnDefinition Width="0.114*"/>
                            </Grid.ColumnDefinitions>
                            <Ellipse StrokeThickness="2"
                                     VerticalAlignment="Stretch"
                                     Grid.ColumnSpan="3"
                                     Grid.RowSpan="3"
                                     x:Name="ellipse"
                                     Fill="{TemplateBinding Background}">
                                <Ellipse.Stroke>
                                    <LinearGradientBrush
                                        EndPoint="0.5,1"
                                        StartPoint="0.5,0">
                                        <GradientStop
                                            Color="#FF6A6A6A"
                                            Offset="0"/>
                                        <GradientStop
                                            Color="#FFFFFFFF"
                                            Offset="0.482"/>
                                        <GradientStop
                                            Color="#FF515151"
                                            Offset="1"/>
                                    </LinearGradientBrush>
                                </Ellipse.Stroke>
                            </Ellipse>
                            <ContentPresenter
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                Grid.ColumnSpan="3"
                                Grid.RowSpan="3"/>
                            <Ellipse Stroke="#FF000000"
                                     StrokeThickness="0"
                                     HorizontalAlignment="Stretch"
                                     Margin="0,0,0,0"
                                     VerticalAlignment="Stretch"
                                     Width="Auto"
                                     Height="Auto"
                                     Grid.Column="1"
                                     Grid.Row="1"
                                     x:Name="ellipse1">
                                <Ellipse.Fill>
                                    <LinearGradientBrush
                                        EndPoint="0.5,1"
                                        StartPoint="0.5,0">
                                        <GradientStop
                                            Color="#D8FFFFFF"
                                            Offset="0"/>
                                        <GradientStop
                                            Color="#15FFFFFF"
                                            Offset="0.845"/>
                                    </LinearGradientBrush>
                                </Ellipse.Fill>
                            </Ellipse>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="FontSize" Value="10"/>
    </Style>
</UserControl.Resources>

The above code contains the definition for the custom button we are going to use. To add a button to your custom control, add the following code in the Grid element:

<Button Content="Notify"
        Height="42"
        HorizontalAlignment="Left"
        Style="{StaticResource ButtonStyle1}"
        VerticalAlignment="Top"
        Width="83"
        Background="{StaticResource Orange_BACKGROUND}"
        FontFamily="Arial Unicode MS"
        Margin="12,12,0,0"
        ContentStringFormat="" />
<Button Background="{StaticResource Orange_BACKGROUND}"
        Content="Increase Limit"
        ContentStringFormat=""
        FontFamily="Arial Unicode MS"
        Height="42"
        Margin="0,12,12,0"
        Style="{StaticResource ButtonStyle1}"
        VerticalAlignment="Top"
        HorizontalAlignment="Right"
        Width="83" />

The above code will add two buttons to our control, we also need textblock to instruct the user how to use the buttons, to do this add the following code below the last button:

<TextBlock Height="110" Margin="12,75,12,0" Name="textBlock1"
           VerticalAlignment="Top" TextWrapping="Wrap" >
    <TextBlock.Text>
        This customer has exceeded their credit limit.
        Choose whether to notify them and the account
        department or automatically increase their limit with 10%
    </TextBlock.Text>
</TextBlock>

If everything went well, your designer should look something like this:

Custom designer

We’ve now created all the WPF elements and need to incorporate it into our Windows Forms User Control. In order to accomplish this, open the user control and add an ElementHost control to it. You can find the ElementHost control under the WPF Interoperability tab of the Visual Studio 2010 toolbar. Dock it in its Parent Container and select our WPF user control from the Hosted Content list. If you do not see the control in the list of items, rebuild your project.

Now that we have our controls in place, open the AddinModule designer and click the ellipses(…) button next to its TaskPanes property. Add a new Task pane and set the ControlProgId property to your Windows forms User Control.

Adding a new task pane

Build and register your project. When Northwind receives an e-mail from a customer, the custom task pane will show whether this customer has reached their credit limit and present the user with two shiny WPF styled buttons.

WPF styled buttons

Thank you for reading. Until next time, keep coding!

Post a comment

Have any questions? Ask us right now!