Extend Outlook Reading pane with custom forms
in VSTO: VB.NET, C#

Add-in Express™ Regions
for Microsoft® Outlook and VSTO


Customizing Outlook Reading pane

To show you how easily you can implement Outlook regions, we'll create two different view regions:

  • Outlook Navigation Pane region that displays a list of recently received emails, please look through this project first if you haven't seen it yet
  • Outlook Reading pane region that shows address details for the sender of the current message, it is described further on this page
Start Visual Studio and create a new Outlook Add-in Project:

Top Reading Pane region: address details

This region will be displayed prominently above the e-mail header in the Outlook Reading Pane and will show address details for the sender of the current message. If the user has sent the e-mail from an Internet/POP3 or other non-Exchange account, AND they have a related Contact item in one of your Contact folders (that is address book enabled), then it will show all three of their e-mail addresses (if they have three!) plus their mailing address. For senders from your Exchange organization, it will only show their primary e-mail address and address details pulled from their Global Address List (GAL) entry. We'll also add some smart functionality so that if there is no related Contact or GAL item, we'll collapse the region so as not to overly bother the user with empty controls.

Building the Outlook Reading Pane region

Once again, we need to add another new Region form to the project with the New Region Wizard: launch the Add New Item dialog and select "ADX Region for Outlook and VSTO":

Select ReadingPane.Top for the Explorer Layout, check MailItem for Explorer Item Types, and then click Next. Click Next again in the next step of the Wizard which is for Inspector regions only, until you get to the final screen:

For the Address Details form, we want it to start Minimized, so select that value for the Default region state. Also clear the "Allow the hidden state" checkbox, check the "Always show header" checkbox, and click Finish.

Next, we need to design the UI of our custom form. Simply add the following controls to ADXOlForm2:

  • Four Label controls
  • Three TextBox controls ("txtEmail1", "txtEmail2", "txtEmail3")
  • A fourth TextBox control ("txtMailingAddress") with Multiline set to True

The layout should look like this in the designer:

Then add the code below to the class. In summary, the code will:

  • Monitor the ADXSelectionChange event which will fire every time an e-mail is selected in the current folder
  • Use the Outlook.Explorer.Selection object to detect what is selected and only process single selections of e-mail item types only
  • Retrieve an Outlook.MailItem object from the Selection object
  • Obtain an Outlook.AddressEntry object from the MailItem.Sender property
  • Process e-mail and address information differently for populating the controls, based on whether the sender is from an Internet account (e.g. POP3 or IMAP) or an Exchange user in the same Exchange Organization
    • If the sender is from the Internet, call the AddressEntry.GetContact method to retrieve a related ContactItem object if an associated Contact item exists for the sender in one of the user's Contacts folders. If it does, use the ContactItem.EmailAddress1 (and EmailAddress2, and EmailAddress3) and ContactItem.MailingAddress properties to populate the controls)
    • If the sender is an Exchange user, call AddressEntry.GetExchangeUser to retrieve an Outlook.ExchangeUser object (which maps to the user's entry in the Global Address List in Exchange). Then use the ExchangeUser.PrimarySmtpAddress property to populate only the "txtEmail1" TextBox control (the user will not have multiple e-mails unless they have other aliases, which we won't handle). Also concatenate the values of the ExchangeUser.StreetAddress, ExchangeUser.City, ExchangeUser.StateOrProvince and ExchangeUser.PostalCode property values for passing to the txtMailingAddress TextBox control, as there is no MailingAddress property equivalent in an ExchangeUser object like there is for a ContactItem object.
  • Collapse or expand the form region as appropriate (set Me.RegionState to AddinExpress.OL.ADXRegionState.Normal or AddinExpress.OL.ADXRegionState.Minimized) if there is no address details for the selected item (or for non-email item types)

VB.NET


					
				Private Sub ADXOlForm2_ADXSelectionChange() Handles Me.ADXSelectionChange
						Dim objAE As Outlook.AddressEntry = Nothing
						Dim objSelection As Outlook.Selection = Nothing
						Dim objExplorer As Outlook.Explorer

						objExplorer = TryCast(ExplorerObj, Outlook.Explorer)

						Try
							'Exception could occur when selecting root folder of store
							objSelection = objExplorer.Selection
						Catch ex As System.Exception
							'Ignore; NULL check below will branch out
						End Try
				 
						'Clear the controls and minimize the form
						txtEmail1.Text = ""
						txtEmail2.Text = ""
						txtEmail3.Text = ""
						txtMailingAddress.Text = ""

						If objSelection Is Nothing Then
							Me.RegionState = AddinExpress.OL.ADXRegionState.Minimized
							Exit Sub
						End If

						If objSelection.Count = 1 Then
							Dim objItem As Object
							Dim objMail As Outlook.MailItem

							objItem = objSelection.Item(1)
							If objItem.Class = Outlook.OlObjectClass.olMail Then
								objMail = TryCast(objItem, Outlook.MailItem)
								If Not objMail Is Nothing Then

									Try
										'Warning: slow Outlook Anywhere connections could cause a
										'problem with this call
										objAE = objMail.Sender
									Catch ex As System.Exception
										System.Windows.Forms.MessageBox.Show(ex.ToString)
										Me.RegionState = AddinExpress.OL.ADXRegionState.Hidden
										GoTo Release1
									End Try

									If objAE Is Nothing Then
										Me.RegionState = AddinExpress.OL.ADXRegionState.Hidden
										GoTo Release1
									End If

									Select Case objAE.AddressEntryUserType
										Case Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry
											Dim objEU As Outlook.ExchangeUser

											objEU = objAE.GetExchangeUser
											If Not objEU Is Nothing Then
												txtEmail1.Text = objEU.PrimarySmtpAddress
												txtEmail2.Text = "N/A"
												txtEmail3.Text = "N/A"
												txtMailingAddress.Text = objEU.StreetAddress & vbCrLf & _
													objEU.City & ", " & objEU.StateOrProvince & "  " & _
													objEU.PostalCode

												Marshal.ReleaseComObject(objEU)
												objEU = Nothing
											End If

											'Raise the Region!
											Me.RegionState = AddinExpress.OL.ADXRegionState.Normal

										Case Outlook.OlAddressEntryUserType.olSmtpAddressEntry
											Dim objContact As Outlook.ContactItem

											objContact = objAE.GetContact

											If Not objContact Is Nothing Then
												txtEmail1.Text = objContact.Email1Address
												txtEmail2.Text = objContact.Email2Address
												txtEmail3.Text = objContact.Email3Address
												txtMailingAddress.Text = objContact.MailingAddress
												Marshal.ReleaseComObject(objContact) : objContact = Nothing

												'Raise the Region!
												Me.RegionState = AddinExpress.OL.ADXRegionState.Normal
											Else
												Me.RegionState = AddinExpress.OL.ADXRegionState.Minimized
											End If
										Case Else
											Me.RegionState = AddinExpress.OL.ADXRegionState.Minimized
									End Select
				Release1:
									objMail = Nothing
									If Not objAE Is Nothing Then
										Marshal.ReleaseComObject(objAE) : objAE = Nothing
									End If
								End If
							Else
								Me.RegionState = AddinExpress.OL.ADXRegionState.Minimized
							End If

							Marshal.ReleaseComObject(objItem) : objItem = Nothing
						Else
							Me.RegionState = AddinExpress.OL.ADXRegionState.Minimized
						End If

						Marshal.ReleaseComObject(objSelection) : objSelection = Nothing
					End Sub
					
					

C#


					
				private void ADXOlForm2_ADXSelectionChange()
					{
						Outlook.AddressEntry objAE = null;
						Outlook.Selection objSelection = null;
						Outlook.Explorer objExplorer = default(Outlook.Explorer);

						objExplorer = ExplorerObj as Outlook.Explorer;

						try
						{
							//Exception could occur when selecting root folder of store
							objSelection = objExplorer.Selection;
						}
						catch (System.Exception ex)
						{
							//Ignore; NULL check below will branch out
						}

						//Clear the controls and minimize the form
						txtEmail1.Text = "";
						txtEmail2.Text = "";
						txtEmail3.Text = "";
						txtMailingAddress.Text = "";

						if (objSelection == null)
						{
							this.RegionState = AddinExpress.OL.ADXRegionState.Minimized;
							return;
						}

						if (objSelection.Count == 1)
						{
							Outlook.MailItem objMail = default(Outlook.MailItem);

							try
							{
								objMail = objSelection[1];
							}
							catch (Exception ex)
							{
								this.RegionState = AddinExpress.OL.ADXRegionState.Hidden;
								return;
							}

							if ((objMail != null))
							{
								try
								{
									//Warning: slow Outlook Anywhere connections could cause a
									//problem with this call
									objAE = objMail.Sender;
								}
								catch (System.Exception ex)
								{
									System.Windows.Forms.MessageBox.Show(ex.ToString());
									this.RegionState = AddinExpress.OL.ADXRegionState.Hidden;
									goto Release1;
								}

								if (objAE == null)
								{
									this.RegionState = AddinExpress.OL.ADXRegionState.Hidden;
									goto Release1;
								}

								switch (objAE.AddressEntryUserType)
								{
									case Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry:
										Outlook.ExchangeUser objEU = default(Outlook.ExchangeUser);

										objEU = objAE.GetExchangeUser();
										if ((objEU != null))
										{
											txtEmail1.Text = objEU.PrimarySmtpAddress;
											txtEmail2.Text = "N/A";
											txtEmail3.Text = "N/A";
											txtMailingAddress.Text = objEU.StreetAddress + 
												Environment.NewLine + 
												objEU.City + ", " + 
												objEU.StateOrProvince + "  " + 
												objEU.PostalCode;

											Marshal.ReleaseComObject(objEU);
											objEU = null;
										}

										//Raise the Region!
										this.RegionState = AddinExpress.OL.ADXRegionState.Normal;

										break;
									case Outlook.OlAddressEntryUserType.olSmtpAddressEntry:
										Outlook.ContactItem objContact = default(Outlook.ContactItem);

										objContact = objAE.GetContact();

										if ((objContact != null))
										{
											txtEmail1.Text = objContact.Email1Address;
											txtEmail2.Text = objContact.Email2Address;
											txtEmail3.Text = objContact.Email3Address;
											txtMailingAddress.Text = objContact.MailingAddress;
											Marshal.ReleaseComObject(objContact);
											objContact = null;

											//Raise the Region!
											this.RegionState = AddinExpress.OL.ADXRegionState.Normal;
										}
										else
										{
											this.RegionState = AddinExpress.OL.ADXRegionState.Minimized;
										}
										break;
									default:
										this.RegionState = AddinExpress.OL.ADXRegionState.Minimized;
										break;
								}
							Release1:
								Marshal.ReleaseComObject(objMail);
								objMail = null;
								if ((objAE != null))
								{
									Marshal.ReleaseComObject(objAE);
									objAE = null;
								}
							}
						}
						else
						{
							this.RegionState = AddinExpress.OL.ADXRegionState.Minimized;
						}

						Marshal.ReleaseComObject(objSelection);
						objSelection = null;
					}
					
					

Design your custom forms and let Advanced Regions do the rest!

That is all that is required for two simple Outlook view regions for Reading pane and Navigation pane. The key thing to remember is that the New Region Wizard does all of the hard work for you. The code that is needed for implementing the region and ensuring that the regions load in the contexts you configured are automatically created. You just need to design your forms and use the provided events and exposed Outlook objects in the forms derived from the AddinExpress.OL.ADXOlForm class, or easily access your regions from the ThisAddin class, to build your final solution.