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:
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:
Next, add a new WPF User Control to your 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:
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.
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.
Thank you for reading. Until next time, keep coding!