Building a Custom Add-in for Outlook 2007 Using Windows Presentation Foundation
Summary: Learn how to build a custom add-in for Microsoft Office Outlook 2007 using the Microsoft .NET Framework 3.0 and Microsoft Windows Presentation Foundation.
Fulvio Giaccari, SB Soft S.r.l.
April 2007
Applies to: Microsoft Office Outlook 2007, Microsoft Visual Studio Tools for Office 2005 SE, Microsoft .NET Framework 3.0, Microsoft Windows Presentation Foundation
Download the code for the custom add-in.
Contents
Building Custom Solutions
Creating the Project
Creating the AddMenuBar Method
Creating the Windows Form
Conclusion
Additional Resources
About the Author
Building Custom Solutions
Although the 2007 Microsoft Office system is both robust and powerful, it cannot solve every complex business problem. That is why the ability to build custom solutions and add-ins for Microsoft Office is a critical component of the 2007 Office system. Using Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System Second Edition (VSTO), you can develop custom solutions to help solve the problems that your business needs to solve.
This article demonstrates how to develop an add-in for Microsoft Office Outlook 2007 to help a fictitious company, the World Tour Travel Agency, generate customer e-mail to confirm flight reservations.
As you build the custom add-in, you will gain experience developing solutions for Office Outlook 2007. This article introduces programming techniques for the Microsoft .NET Framework 3.0 and shows you how to use the Windows Presentation Foundation (WPF) to create the user interface controls that the custom add-in uses.
The custom add-in contains two parts: the first part handles linking the add-in to Office Outlook 2007; the second part handles the add-in's graphical layout. Although this add-in does not interact with a Microsoft SQL Server database, imagine how much more robust you can make it by adding database code. Figure 1 shows the class schema for the custom add-in.
Figure 1. Custom add-in class schema
Creating the Project
To start, create an Office Outlook 2007 project in VSTO and call it WorldTravelAddIn (see Figure 2).
Figure 2. The Outlook 2007 add-in project
Notice that the solution has a project called WorldTravelAddinSetup that allows you to install the add-in on any computer that has Outlook 2007 and Windows Presentation Foundation installed.
The project has a file called ThisAddIn.cs (or ThisAddIn.vb if you created a Visual Basic project). The ThisAddIn class has two methods that manage events: ThisAddIn_Startup and ThisAddIn_Shutdown. ThisAddIn_Startup contains method calls that execute before Outlook 2007 is launched; ThisAddIn_Shutdown contains method calls that execute when Outlook 2007 is closed.
ThisAddIn.cs imports the following namespaces:
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
Creating the AddMenuBar Method
Next, add a method to the project to create a button inside the Outlook 2007 menu. When the button is pressed, Outlook displays a form that helps the user create a customized e-mail.
To request the classes that the add-in uses, add the following code to ThisAddIn.cs:
private Office.CommandBar menuBar;
private Office.CommandBarPopup newMenuBar;
private Office.CommandBarButton buttonOne;
private string menuTag = "WorldAddIn";
After you request the classes, add the AddMenuBar method to create a menu item in Outlook 2007. Add the following code to ThisAddIn.cs:
private void AddMenuBar()
{
}
The AddMenuBar method contains code that creates the menu and inserts it into an existing Outlook 2007 menu. The following code adds both a new menu item and a button that opens the WPF form:
Try
{
//Define the existent Menu Bar
menuBar = this.Application.ActiveExplorer().CommandBars.ActiveMenuBar;
//Define the new Menu Bar into the old menu bar
newMenuBar = (Office.CommandBarPopup)menuBar.Controls.Add(
Office.MsoControlType.msoControlPopup, missing,
missing, missing, false);
//If I dont find the newMenuBar, I add it
if (newMenuBar != null)
{
newMenuBar.Caption = "World Tour Agency";
newMenuBar.Tag = menuTag;
buttonOne = (Office.CommandBarButton)newMenuBar.Controls.
Add(Office.MsoControlType.msoControlButton, missing,
missing, 1, true);
buttonOne.Style = Office.MsoButtonStyle.
msoButtonIconAndCaption;
buttonOne.Caption = "Send Confirmation Email";
//This is the Icon near the Text
buttonOne.FaceId = 610;
buttonOne.Tag = "c123";
//Insert Here the Button1.Click event
newMenuBar.Visible = true;
}
}
catch (Exception ex)
{
//This MessageBox is visible if there is an error
System.Windows.Forms.MessageBox.Show("Error: " + ex.Message.ToString(), "Error Message Box", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
After you create the AddMenuBar method, add a call to it inside ThisAddIn_Startup:
//Method to create new menu
AddMenuBar();
Note |
---|
If you launch the add-in in debug mode more than once, the add-in menu is inserted into the Outlook 2007 menu multiple times. To prevent this from happening, the add-in needs a RemoveMenubar method. |
Next, add the RemoveMenubar method; this method checks to see if the add-in menu already exists inside Outlook 2007. If the add-in menu exists, this method deletes it. Add the following code to ThisAddIn.cs:
#region Remove Button from Outlook's Menu
private void RemoveMenubar()
{
// If the menu already exists, remove it.
try
{
Office.CommandBarPopup foundMenu = (Office.CommandBarPopup)
this.Application.ActiveExplorer().CommandBars.ActiveMenuBar.
FindControl(Office.MsoControlType.msoControlPopup,
missing, menuTag, true, true);
if (foundMenu != null)
{
foundMenu.Delete(true);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
#endregion
Next, place a call to this method inside ThisAddIn_Startup before the call to AddMenuBar:
//Search the menu and delete it if found
RemoveMenubar();
Next, the add-in needs to handle button-click events. Add the following code to AddMenuBar:
buttonOne.Click += new Office._CommandBarButtonEvents_ClickEventHandler(buttonOne_Click);
After you associate an event handler to the button click, add the buttonOne_Click method to the ThisAddIn class:
#region Create and Open WPF form
/// <param name="ctrl">Create WPF Form runtime</param>
private void buttonOne_Click(Office.CommandBarButton ctrl, ref bool cancel)
{
}
#endregion
Creating the Windows Form
The second part of the add-in creates the Windows Form to help generate a customer e-mail. To add an .ASMX form file to the project, right click the WorldTravelAddIn project, click Add and then click New Item (see Figure 3).
Figure 3. Insert a form file to the add-in
Next, click Windows(WPF) and name the form file window2.xaml; then click Add (see Figure 4).
Figure 4. Add a WPF file to the project
After you add the form files, Visual Studio 2005 SE inserts all the namespaces that the add-in needs for the form to work correctly with WPF.
Note |
---|
The namespaces that the add-in needs to function correctly in Outlook 2007 are automatically added during VSTO setup. |
After all the necessary references and namespaces are added, your VSTO project should look like Figure 5.
Figure 5. The VSTO project
The custom add-in creates all of the form controls at runtime; this is one aspect of how control management differs in .NET Framework 3.0.
Add the FormWPFCreate method that will contain the form controls:
private void FormWPFCreate()
{
}
Inside this method, add a request for the form window and set the initial dimensions and title of the form:
//Create a Windows form with WPF namespace
Window2 window = new Window2();
window.Title = "Form to create a Mail";
//Set the dimension
window.Height = 306;
window.Width = 464;
Next, add a Grid control to which the form controls will link. It is important to know that every time you define a form with WPF, you must also define a Grid component:
//Create the Grid Control
Grid newgrid = new Grid();
//Show the Grid Lines
newgrid.ShowGridLines = true;
Next, define the lines inside the newgrid component. Like all grids, the newgrid component has both rows and columns. The add-in defines two rows and zero columns:
//Define Row of Grid
RowDefinition rowDef1 = new RowDefinition();
rowDef1.Height = new GridLength(102, GridUnitType.Pixel);
RowDefinition rowDef2 = new RowDefinition();
rowDef2.Height = new GridLength(171, GridUnitType.Pixel);
//Add the row to the grid component
newgrid.RowDefinitions.Add(rowDef1);
newgrid.RowDefinitions.Add(rowDef2);
Note |
---|
Rows and columns are measured in units of star or pixel. If the rows and columns are created at design time, VSTO automatically uses star as the measurement unit. If you want to measure units in pixels, you must click in the point indicated by the red circle in Figure 6. |
Figure 6. Changing measurement units
After the lines in the Grid component are defined, the add-in needs to define a NameScope object.
Namescope objects are both a concept, and also the programming objects that store relationships between the XAML defined names of objects and their instance equivalents. Namescopes in the WPF managed code are created while loading the pages for a XAML application. Namescopes as the programming object are defined by the INameScope interface and are also implemented by the practical class NameScope.
To define the Namescope add the following code to the add-in source code:
// Create a name scope for the Grid
// and assign a name
NameScope.SetNameScope(newgrid, new NameScope());
Next, add all the controls that the form needs. Start with the first label:
#region Label Travel Agency
System.Windows.Controls.Label label1 = new System.Windows.Controls.Label();
label1.Height = 32.2766666666667;
label1.Margin = new System.Windows.Thickness(113.37, 4.7233333333333, 0, 0);
label1.VerticalAlignment = System.Windows.VerticalAlignment.Top;
label1.FontSize = 16;
label1.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
label1.Width = 112.63;
label1.Content = "Travel Agency";
// Add to Grid the component Label1
Grid.SetRow(label1, 0);
#endregion
To better understand what the code is doing, examine it closely. First, the code creates an instance of the Label object in the namespace System.Windows.Controls. This namespace is different from the namespace used in .NET Framework 2.0.
Next, the code sets the label's Height property and Margin property. The Margin property is a new concept in .NET Framework 3.0; it pinpoints the control's position. Without this information the control cannot be positioned.
The last control that the code inserts associates the Grid component of the form to the component label1 just created. If the code does not associate the control to the component Grid, the control will not display properly.
Next, add the other form components:
#region Label World Tour
System.Windows.Controls.Label label2 = new System.Windows.Controls.Label();
label2.Height = 34.2766666666667;
label2.Margin = new System.Windows.Thickness(184.37, 27.7233333333333, 142, 40.0000000000001);
label2.VerticalAlignment = System.Windows.VerticalAlignment.Top;
label2.FontSize = 20;
System.Windows.Media.FontFamily myFont = new System.Windows.Media.FontFamily("Arial");
label2.FontFamily = myFont;
label2.Foreground = Brushes.Blue;
label2.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
label2.Width = 125.63;
label2.Content = "World Tour";
Grid.SetRow(label2, 0);
#endregion
#region Image Travel Agency
System.Windows.Controls.Image image1 = new System.Windows.Controls.Image();
image1.Width = 100;
System.Windows.Media.Imaging.BitmapImage imageSource = new System.Windows.Media.Imaging.BitmapImage();
imageSource.BeginInit();
//Put your directory where is located
//your png image
imageSource.UriSource = new Uri(@"C:\Fulvio\img\yast_suse_tour.png");
imageSource.DecodePixelHeight = 100;
imageSource.EndInit();
image1.Margin = new System.Windows.Thickness(-180, 6, 175, 8);
image1.Source = imageSource;
//image1.Height = 88;
image1.VerticalAlignment = System.Windows.VerticalAlignment.Top;
image1.Stretch = Stretch.Uniform;
Grid.SetRow(image1, 0);
#endregion
#region Label "Fill Form"
System.Windows.Controls.Label label3 = new System.Windows.Controls.Label();
label3.Height = 23.2766666666667;
label3.Margin = new System.Windows.Thickness(20.37, 5.93852320675109, 0, 0);
label3.VerticalAlignment = System.Windows.VerticalAlignment.Top;
label3.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
label3.Width = 86.63;
label3.Content = "Fill Form:";
Grid.SetRow(label3, 1);
#endregion
#region Label Name
System.Windows.Controls.Label label4 = new System.Windows.Controls.Label();
label4.Height = 23.2766666666667;
label4.Margin = new System.Windows.Thickness(18.37, 28.938523206751, 0, 0);
label4.VerticalAlignment = System.Windows.VerticalAlignment.Top;
label4.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
label4.Width = 70.63;
label4.Content = "Name: ";
Grid.SetRow(label4, 1);
#endregion
#region Label Sourname
System.Windows.Controls.Label label5 = new System.Windows.Controls.Label();
label5.Margin = new System.Windows.Thickness(17.37, 53.938523206751, 0, 93.7848101265823);
label5.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
label5.Width = 71.63;
label5.Content = "Sourname: ";
Grid.SetRow(label5, 1);
#endregion
#region Label Email
System.Windows.Controls.Label label6 = new System.Windows.Controls.Label();
label6.Margin = new System.Windows.Thickness(18.3699999999999, 77.7233333333333, 0, 70);
label6.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
label6.Width = 35.63;
label6.Content = "Email:";
Grid.SetRow(label6, 1);
#endregion
#region Name's TextBox
System.Windows.Controls.TextBox TextBox1 = new System.Windows.Controls.TextBox();
TextBox1.Height = 19;
TextBox1.Width = 100;
TextBox1.Name = "Name";
// Register TextBox2's name with newgrid
newgrid.RegisterName(TextBox1.Name, TextBox1);
TextBox1.Margin = new System.Windows.Thickness(140, 31, 0, 0);
TextBox1.VerticalAlignment = System.Windows.VerticalAlignment.Top;
TextBox1.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
Grid.SetRow(TextBox1, 1);
#endregion
#region Sourname's TextBox
System.Windows.Controls.TextBox TextBox2 = new System.Windows.Controls.TextBox();
TextBox2.Height = 19;
TextBox2.Width = 100;
TextBox2.Name = "Sourname";
// Register TextBox2's name with newgrid
newgrid.RegisterName(TextBox2.Name, TextBox2);
TextBox2.Margin = new System.Windows.Thickness(140, 55, 0, 0);
TextBox2.VerticalAlignment = System.Windows.VerticalAlignment.Top;
TextBox2.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
Grid.SetRow(TextBox2, 1);
#endregion
#region Email's TextBox
System.Windows.Controls.TextBox TextBox4 = new System.Windows.Controls.TextBox();
TextBox4.Height = 19;
TextBox4.Width = 100;
TextBox4.Name = "Email";
//Register TextBox3's Name with newgrid
newgrid.RegisterName(TextBox4.Name, TextBox4);
TextBox4.Margin = new System.Windows.Thickness(141, 80, 0, 72);
TextBox4.VerticalAlignment = System.Windows.VerticalAlignment.Top;
TextBox4.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
Grid.SetRow(TextBox4, 1);
#endregion
#region Label Copyright
System.Windows.Controls.Label labelCopyright = new System.Windows.Controls.Label();
labelCopyright.Content = "Copyrights 2006-2007 Fulvio Giaccari";
labelCopyright.Background = Brushes.BurlyWood;
labelCopyright.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
labelCopyright.Margin = new Thickness(0, 1.7233333333333, -266, 0);
labelCopyright.Width = 269.63;
labelCopyright.Height = 24.2766666666667;
labelCopyright.VerticalAlignment = VerticalAlignment.Top;
labelCopyright.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center;
// Create a RotateTransform to rotate
// the labelCopyright 90 degrees about its
// top-left corner.
RotateTransform labelCopyrightRotateTransform = new RotateTransform(90);
labelCopyright.RenderTransform = labelCopyrightRotateTransform;
Grid.SetRow(labelCopyright, 0);
#endregion
The add-in displays some of the new components and abilities that are available in .NET Framework 3.0. For example, the code uses RotateTransform to rotate the labelCopyright component.
Note |
---|
The add-in uses an image file stored on your hard drive as C:\Fulvio\img\yast_suse_tour.png. You need to supply your own image file, otherwise the form will not display. |
Next, add the Button component. This component is added last because it requires more complex management than the other components. To define the button control, add the following code:
#region Button
System.Windows.Controls.Button button1 = new System.Windows.Controls.Button();
button1.Height = 39;
button1.Margin = new System.Windows.Thickness(14, 0, 0, 16);
button1.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
button1.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
button1.Width = 191;
button1.Content = "Create";
button1.Click += new RoutedEventHandler(button1_Click);
Grid.SetRow(button1, 1);
#endregion
Next, add the event handler for the button control:
#region Button1 Event
void button1_Click(object sender, RoutedEventArgs e)
{
try
{
System.Windows.Controls.Button btn1 = sender as System.Windows.Controls.Button;
Grid grid1 = (Grid)btn1.Parent;
//Find from NameScope all TextBox controls
System.Windows.Controls.TextBox txtName = (System.Windows.Controls.TextBox)grid1.FindName("Name");
System.Windows.Controls.TextBox txtSourname = (System.Windows.Controls.TextBox)grid1.FindName("Sourname");
System.Windows.Controls.TextBox txtEmail = (System.Windows.Controls.TextBox)grid1.FindName("Email");
//Call SendEmail Method to create email
SendEmail(txtName.Text, txtSourname.Text, txtEmail.Text);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show("Error: " + ex.Message.ToString());
}
}
#endregion
This method demonstrates the potential of the NameScope class. Notice that the code links some controls (the TextBox controls) to the NameScope class in such a way as to call them back at any time.
The button1_Click method just defined calls a method called SendEmail. To avoid a compiler error, define the SendEmail method as follows:
#region Create Email
private void SendEmail(string name, string sourname, string email)
{
string _body;
_body = "Dear Mr. <STRONG>%name%</STRONG> <STRONG>%sourname%</STRONG>, <BR /> Your flight is now booked. Thank you for using our services. <BR /><BR /> Vincent Lauriat<BR />'World Tour' Travel Agency";
StringBuilder sb = new StringBuilder(_body);
sb.Replace("%name%", name.ToString());
sb.Replace("%sourname%", sourname.ToString());
Outlook.Application app1 = new Microsoft.Office.Interop.Outlook.Application();
Outlook.MailItem mail1 = (Outlook.MailItem)
app1.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
mail1.To = email;
mail1.Subject = "Your flight is booked";
mail1.BodyFormat = Microsoft.Office.Interop.Outlook.OlBodyFormat.olFormatHTML;
mail1.HTMLBody = sb.ToString();
mail1.Display(true);
}
#endregion
This method creates a customized e-mail with the runtime data that the user enters into the form.
The last part of the add-in manages how the controls inside the newgrid component of the FormWPFCreate method are displayed. In the FormWPFCreate method, add the following code after the code that creates the Button1 control:
Try
{
//Add all controls to Grid
newgrid.Children.Add(button1);
newgrid.Children.Add(label1);
newgrid.Children.Add(label2);
newgrid.Children.Add(label3);
newgrid.Children.Add(label4);
newgrid.Children.Add(label5);
newgrid.Children.Add(label6);
newgrid.Children.Add(image1);
newgrid.Children.Add(TextBox1);
newgrid.Children.Add(TextBox2);
newgrid.Children.Add(TextBox4);
newgrid.Children.Add(labelCopyright);
window.Content = newgrid;
window.Show();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message.ToString());
}
Finally, add the following call to the FormWPFCreate method inside the buttonOne_Click method that handles events for the add-in menu:
private void buttonOne_Click(Office.CommandBarButton ctrl, ref bool cancel)
{
FormWPFCreate();
}
Launch the application in debug mode to see the add-in menu (see Figure 7).
Figure 7. World Tour Agency custom menu
When you click the button inside the menu, you then see the Windows Form that the add-in creates at runtime (see Figure 8).
Figure 8. World Tour Agency custom form
Fill in all the blanks in the form and click Create; the add-in then creates a customized e-mail using the form data (see Figure 9).
Figure 9. Customized customer e-mail
Conclusion
This article showed you how to develop a custom add-in for Outlook 2007 using Visual Studio 2005 Tools for Office SE. Although the add-in was relatively simple, you gained a sense of the combined potential of VSTO and Office Outlook 2007.
The add-in demonstrated how to use the Windows Presentation Foundation component of the .NET Framework 3.0 to create a form. The form added many controls at runtime and used the NameScope class to associate controls to user interface events that the form generated at runtime.
Finally, the add-in showed that when you use the .NET Framework 3.0 to create a form, the form contains a Grid control containing rows and columns and that you must associate every form control with a specific row or column of the Grid control.
Additional Resources
About the Author
Fulvio Giaccari is a Project Manager for SB Soft S.r.l., a Microsoft partner company. He is currently working on porting Windows Form applications to the .NET Framework 3.0. Fulvio is a published author of magazine articles in both English and Italian; he plans to write a book about VSTO that will be published by Apress Inc.
Fulvio is founder of www.freeaspx.it, an online community dedicated to ASP.NET. He is also founder of www.ShareOffice.it, the first Italian user group for Microsoft Office developers.
SB Soft S.r.l. is an Italian company that specializes in building solutions for Microsoft Office and Microsoft SharePoint Services as well as building Windows Form and Web applications for companies, banks, and public administrations.