Working with Outlook Form Regions
Introduction to Form Regions
In Outlook 2007, developers have the ability to extend the Outlook UI by creating a special kind of Outlook extension called an Outlook form region. Form regions are used primarily to customize Inspector windows, which we introduced in Chapter 10, “Working with Outlook Events.” Inspector windows are the Outlook windows that appear when you double-click an Outlook item—a mail item in your inbox or a task in a task list, for example. With form regions you can do things like add pages to the Inspector window, replace all the existing pages in an Inspector window with your own page, or dock some custom UI onto an existing page. You can also use a certain type of Outlook form region (an Adjoining form region) to customize the reading pane in Outlook Explorer windows.
Creating a New Form Region
To begin our exploration of Outlook form regions, let’s create a simple one by using Visual Studio 2008. Start by creating a new Outlook add-in project by choosing File > New > Project. In the New Project dialog box that appears, create a new Outlook 2007 add-in, as shown in Figure 16-1.
Figure 16-1 Creating a new Outlook 2007 add-in.
Now, in your new add-in project, choose Project > Add New Item. Click the Office category to filter to show just the Office-specific items. In the list of Office items, click Outlook Form Region, as shown in Figure 16-2. Name the form region—just use the default name FormRegion1. Then click the Add button.
Figure 16-2 Adding an Outlook form region to an Outlook 2007 add-in project.
A wizard appears, as shown in Figure 16-3. The first step in the wizard is to decide whether you want to create an Outlook form region or import a form region that was previously designed in Outlook with Outlook’s built-in form designer. For this introduction, click Design a New Form Region. This option lets you use Windows Forms and keeps our editing experience within Visual Studio. Later in the chapter we show you how to use the Outlook built-in form designer, as well as discuss when you might want to use Outlook’s form designer instead of Windows Forms.
Figure 16-3 Selecting the form technology to use to create the form region.
After you decide whether to design a new form region with Windows Forms or to import an existing Outlook form region designed in Outlook, click the Next button to move to the second page of the wizard, shown in Figure 16-4, which allows you to pick the type of form region you want to create.
Figure 16-4 Selecting the type of form region to create: Separate.
To understand the types of form regions that are available in Figure 16-4, we must take a step back and discuss Inspector windows in some additional detail. Form regions are used primarily in Outlook Inspector windows. An Outlook Inspector window can have multiple pages associated with it, and Ribbon buttons are used to switch between the pages associated with a particular Inspector window. Consider the Inspector window that appears when you double-click an Outlook task, as shown in Figure 16-5.
Figure 16-5 A task Inspector window with the Task page selected.
Figure 16-5 has two Ribbon buttons in the Show group: Task and Details. In Figure 16-5 the Task button is selected and the Task page is displayed. The Task page is the default page for the Task Inspector window and is displayed first whenever a task is opened. If you click the Details button, the view changes to the Details page, as shown in Figure 16-6.
Figure 16-6 A Task Inspector window with the Details page selected.
With this background, you’re ready to go back to Figure 16-4 and make sense of the options. A Separate form region adds a new page (and a new Ribbon button to activate that page) to an Inspector window. So you could add a new page to the Task Inspector window to show something like subtasks that must be completed to finish the main task. In Figure 16-4 the wizard also displays a nice graphic to help you remember what a Separate form region is. In this case the graphic emphasizes that you get a new Ribbon button to display the new page, and you have complete control of the new page that is shown.
Figure 16-7 shows what the wizard displays when you select Replacement instead of Separate as the type of form region. A Replacement form region allows you to replace the default page of the Inspector window. So in the task example, you could replace the Task page (the default page for a Task Inspector window), but the Details page would still be available.
Figure 16-7 Selecting the type of form region to create: Replacement.
Figure 16-8 shows what the wizard displays when you select Replace-All as the type of form region. A Replace-All form region allows you to replace all available pages and make your page available only in the Inspector window. So in the task example, you could replace both the Task page and the Details page; your page would be the only page displayed in the Inspector window.
Figure 16-8 Selecting the type of form region to create: Replace-All.
When you think about Replacement and Replace-All form region types, you realize that replacing the default pages for an Outlook item type is a pretty powerful capability—actually too powerful, in a way, because you could change the default page for an Outlook item type, such as a task, and implement a new default page that prevents the user from editing key data associated with that task. You may forget to provide a way to set the priority of a task in your Replacement or Replace-All form region, for example. Indeed, the creators of Outlook didn’t want to give you quite that much power, enough to possibly break key functionality of Outlook.
To jump ahead a little, select Replacement or Replace-All as the form region type and then skip two steps ahead in the wizard by clicking the Next button twice. You see the wizard page shown in Figure 16-9, where you determine which Outlook message classes you want this form region to be associated with. When you select Replacement or Replace-All, notice that all the standard message classes (Appointment, Contact, Task, and so on) are grayed out in this dialog box. Outlook won’t let you replace the default page or replace all the pages for standard message classes because you may break key features of Outlook. To use Replacement and Replace-All form region types, you must define a custom message class. A custom message class can reuse all the existing functionality of a built-in message class such as Appointment, Contact, or Task and acts as a specialized version of those built-in Outlook item objects. We discuss working with custom message classes in more detail later in this chapter, in the section “Form Region Types and Custom Message Classes,” because you must understand that concept to use Replacement and Replace-All form region types.
Figure 16-9 Replacement and Replace-All form regions can be associated only with custom message classes.
Moving back to the page in the wizard where you pick the form region type, consider the final form region type: Adjoining, shown in Figure 16-10. An Adjoining form region is appended to the bottom of the default page for an Inspector. Multiple adjoining form regions can be associated with the same message class, so potentially you can have several Adjoining form regions displayed in one Inspector window’s default page. Adjoining form regions have headers that allow them to be collapsed and expanded to make more room in the default page when needed.
Figure 16-10 Selecting the type of form region to create: Adjoining.
Another interesting application of an Adjoining form region is in an Explorer window. Specifically, an Adjoining form region can be used in the reading pane that is displayed in an Explorer window. In much the same way that they are used in the default page of an Inspector window, multiple Adjoining form regions can be associated with an Outlook message class and can be displayed in the reading pane. Form regions displayed in the reading pane can also be collapsed to their headers. Replacement and Replace-All form regions can be used in the reading pane as well, although in this case they replace what is shown in the reading page and can be used only for custom message classes.
Now that you’re familiar with all the form region types, select Adjoining as the form region type and click the Next button to move to the next page of the wizard, shown in Figure 16-11. In this dialog box, you set the name for the form region that will be displayed in the UI, so pick a friendly name. Title and Description are grayed out because you’re creating an Adjoining form region; those options are enabled only for Replacement and Replace-All form region types.
Figure 16-11 Setting descriptive text and display preferences.
This page of the wizard also has three check boxes that specify when the form region is displayed. The first check box sets whether the form region is displayed for an Inspector window that is in compose mode. An Inspector window is in compose mode when you create a new instance of the Outlook item associated with it—when you create a new task, for example. The second check box sets whether the form region is displayed for an Inspector window that is in read mode. An Inspector window is in read mode when you open an existing item—a mail message, for example. Finally, the third check box sets whether to display the form region in reading-pane view.
For this example, keep all the boxes checked and click the Next button to pick which Outlook message classes to associate the form region with, as shown in Figure 16-12. For this example, select Task. Note that you can associate the same form region with multiple built-in Outlook message classes. You could have a form region that displays for both Tasks and Mail messages, for example. You can also associate a form region with custom message classes, which we discuss later in this chapter. As we describe earlier in this section, Replacement and Replace-All form region types can be associated only with custom message classes.
Figure 16-12 Picking which message classes will display a form region.
Associate the form region with the built-in Task type, and click the Finish button to exit the wizard. Visual Studio creates a new project item called FormRegion1.cs, as shown in Figure 16-13. It displays a visual designer in which you can drag and drop Windows Forms controls from the toolbox to construct the form region. This visual designer is much like the one you use to design user controls and task panes.
Figure 16-13 The newly created form region project item in visual design view.
Customizing a Form Region
Your goal is to add a form region in which subtasks can be associated with a task. First, drag and drop a list box control and a button to create a new task and delete an existing task. Because the user can resize the form region, use the Anchor property of the controls to anchor the list box to the top, left, bottom, and right, and anchor the buttons to the bottom and left. Figure 16-14 shows the final form region.
Figure 16-14 A simple form region.
Before you go any further, run the add-in project and see what happens. Press F5 to build and run Outlook with the add-in project loaded. If you click a task in a task list and show reading view (by choosing View > Reading Pane > Bottom), you see that the adjoining form region is displayed docked at the bottom of reading-pane view for a task, as shown in Figure 16-15. If you double-click a task, the Adjoining form region is docked at the bottom of the default page for the Inspector window, as shown in Figure 16-16. After you’ve run your project, if you want to remove the form region and add-in from Outlook, choose Build > Clean.
Figure 16-15 An Adjoining form region in the reading pane.
Figure 16-16 An Adjoining form region in the default page of an Inspector window.
Let’s examine the adjoining form region a little more. First, notice that the Name you specified in Figure 16-11 is displayed as the caption above the Adjoining form region. To the left of the form region caption is a −/+ button that expands and collapses the form region. In Figure 16-17 you see what an Adjoining form region looks like when it is collapsed. Remember that several Adjoining form regions could be displayed in one Inspector window or reading pane; the ability to expand and collapse them is important, because it allows the end user to manage screen real estate.
Figure 16-17 A collapsed Adjoining form region.
Also, notice that when you resize the reading pane or the Inspector window, the form region has a default height. When the user adjusts the size of the form region, Outlook remembers the height and uses that height the next time the reading view is displayed. If you size the window small enough that the default height of the form region can’t be displayed, a vertical scroll bar appears, as shown in Figure 16-18. This minimum height represents the height you set when you designed the form region. To have a smaller or larger minimum height, simply adjust the height of the visual design surface for the form region inside Visual Studio.
Figure 16-18 The effect of default height on the form region’s vertical scroll bar.
Now exit Outlook and go back to the add-in project to put some code behind the form region. Right-click FormRegion1.cs in the Solution Explorer, and choose View Code from the context menu. The default code for a form region is shown in Listing 16-1. There are three event handlers of interest in our class FormRegion1. The first is actually in a nested class called FormRegion1Factory. This nested class provides a method called FormRegion1Factory_FormRegionInitializing where you can write code to decide whether to show the form region for a given Outlook item. The FormRegionInitializing event handler is passed a parameter e of type FormRegionInitializingEventArgs that can be used to get the Outlook item that the form region is about to be shown for (e.OutlookItem) and to cancel the showing of the form region if necessary by setting e.Cancel to true. Don’t hold a reference to the Outlook item (e.OutlookItem) that is about to be shown; it is provided for use only during the event handler.
The form region class itself (FormRegion1) has a FormRegionShowing event handler that is invoked before the form region is displayed (but too late to prevent the display of the form region altogether; that is what FormRegionInitializing is for). In the FormRegionShowing event handler, you can write code to initialize your form region. In this event handler, you can use the property this.OutlookItem to access the Outlook item associated with the form region.
When the form region is closed, the FormRegionClosed event handler is invoked. This event handler is a good place to save any changes made to the Outlook item by your form region and to do any final cleanup.
Listing 16-1. The Default Code in a New Windows Forms-Based Form Region
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Office = Microsoft.Office.Core; using Outlook = Microsoft.Office.Interop.Outlook; namespace OutlookAddIn1 { partial class FormRegion1 { #region Form Region Factory [Microsoft.Office.Tools.Outlook. FormRegionMessageClass(Microsoft.Office.Tools.Outlook. FormRegionMessageClassAttribute.Task)] [Microsoft.Office.Tools.Outlook. FormRegionName("OutlookAddIn1.FormRegion1")] public partial class FormRegion1Factory { // Occurs before the form region is initialized. // To prevent the form region from appearing, set e.Cancel // to true. Use e.OutlookItem to get a reference to the // current Outlook item. private void FormRegion1Factory_FormRegionInitializing( object sender, Microsoft.Office.Tools.Outlook. FormRegionInitializingEventArgs e) { } } #endregion // Occurs before the form region is displayed. // Use this.OutlookItem to get a reference to the current // Outlook item. Use this.OutlookFormRegion to get a reference // to the form region. private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e) { } // Occurs when the form region is closed. // Use this.OutlookItem to get a reference to the current // Outlook item. Use this.OutlookFormRegion to get a reference // to the form region. private void FormRegion1_FormRegionClosed(object sender, System.EventArgs e) { } } }
Listing 16-2 shows a simple implementation for the subtasks form region. You don’t need to write any code in FormRegionInitializing because you always want to display your form region. In FormRegionShowing, write some code to get a custom UserProperty object from the Outlook item with which the form region is associated. The custom UserProperty we will associate with the Outlook item will have the identifier "SubTasks" You’ll use this custom UserProperty to store the subtasks that are edited by the form region. If the UserProperty isn’t associated with the Outlook item yet, create the UserProperty for the Outlook item in FormRegionInitializing. The "SubTasks" user property contains a string value that contains subtasks delimited by a new line. You parse any subtasks that are in the string and populate the list box for the form region with the subtasks.
In FormRegionClosed, you do the reverse: Grab all the entries out of the list box and concatenate them into a string in which subtasks are separated by new lines. If the subtasks have been changed, set the "SubTasks" UserProperty’s value to the new string and save the associated Outlook item.
Finally, a simple implementation for the Add button just adds the current time as a new subtask; a complete implementation would include a dialog box with an edit box in which the user could type a subtask description. The Delete button deletes the selected list item.
Listing 16-2. Form Region Code for a Simple Subtasks Form Region Based on Windows Forms
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Office = Microsoft.Office.Core; using Outlook = Microsoft.Office.Interop.Outlook; namespace OutlookAddIn1 { partial class FormRegion1 { Outlook.TaskItem task; Outlook.UserProperty subtasks; #region Form Region Factory [Microsoft.Office.Tools.Outlook. FormRegionMessageClass(Microsoft.Office.Tools.Outlook. FormRegionMessageClassAttribute.Task)] [Microsoft.Office.Tools.Outlook. FormRegionName("OutlookAddIn1.FormRegion1")] public partial class FormRegion1Factory { // Occurs before the form region is initialized. // To prevent the form region from appearing, set e.Cancel // to true. Use e.OutlookItem to get a reference to the // current Outlook item. private void FormRegion1Factory_FormRegionInitializing( object sender, Microsoft.Office.Tools.Outlook. FormRegionInitializingEventArgs e) { } } #endregion // Occurs before the form region is displayed. // Use this.OutlookItem to get a reference to the current // Outlook item. Use this.OutlookFormRegion to get a reference // to the form region. private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e) { task = this.OutlookItem as Outlook.TaskItem; if (task != null) { // Check for custom property SubTasks subTasks = task.UserProperties.Find("SubTasks", true); if (subTasks == null) { subTasks = task.UserProperties.Add("SubTasks", Outlook.OlUserPropertyType.olText, false, Outlook.OlUserPropertyType.olText); } } // Convert string string subTasksString = subTasks.Value.ToString(); if (!String.IsNullOrEmpty(subTasksString)) { string[] delimiters = new string[1]; delimiters[0] = System.Environment.NewLine; string[] tasks = subTasksString.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < tasks.Length; i++) { listBoxSubTasks.Items.Add(tasks[i]); } } } // Occurs when the form region is closed. // Use this.OutlookItem to get a reference to the current // Outlook item. Use this.OutlookFormRegion to get a reference // to the form region. private void FormRegion1_FormRegionClosed(object sender, System.EventArgs e) { if (subTasks == null || task == null) return; string oldTasks = subTasks.Value.ToString(); StringBuilder builder = new StringBuilder(); foreach (object o in listBoxSubTasks.Items) { string t = o as string; if (!String.IsNullOrEmpty(t)) { builder.AppendLine(t); } } string newTasks = builder.ToString(); if (!String.IsNullOrEmpty(newTasks) && !String.IsNullOrEmpty(oldTasks)) { if (newTasks.CompareTo(oldTasks) == 0) return; // no changes } subTasks.Value = newTasks; task.Save(); } private void buttonNew_Click(object sender, EventArgs e) { // Just add current time as a subtask for simplicity listBoxSubTasks.Items.Add( System.DateTime.Now.ToShortTimeString()); } private void buttonDelete_Click(object sender, EventArgs e) { if (listBoxSubTasks.SelectedItem != null) { listBoxSubTasks.Items.RemoveAt( listBoxSubTasks.SelectedIndex); } } } }
When you run the form region, it displays as before, but now the Add and Delete buttons work, and you can add subtasks (set to the current time) to the current task.