Customize Outlook Navigation Pane
with .NET forms: VB.NET, C#

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


Customizing Outlook Navigation Pane

Add-in Express Regions allows adding .NET forms into 23 layouts of Outlook windows. This example demonstrates how to embed your custom .NET form under the Outlook Navigation pane using Visual Studio.

Once you have Add-in Express Regions installed, it's very easy to get started integrating Outlook Regions into your VSTO project. Let's start right from the beginning - start Visual Studio and create a new Outlook 2010 Add-in Project (if you like, you can choose an Outlook 2007 Add-in; no references specific to the 2010 Object Model are used in the following examples):

For this demonstration we're going to build an add-in that will highlight how to implement View Regions and show how useful they can be for designing creative Outlook solutions. We'll create two different View Regions:

  • Navigation Pane region that displays a list of recently received emails, it is described further on this page
  • Reading pane region, which shows address details for the sender of the current message

Outlook Navigation Pane region: latest mail

This region will show a "sticky" list of all e-mails received since Outlook was launched. It will start out empty, but as new Outlook e-mails are received they will be added to a ListBox. Double-clicking an entry in the ListBox will open the e-mail. Imagine you often spend time in folders other than the Inbox, and need to frequently go back to the Inbox to find an e-mail and open it. Now with this region always displayed no matter what folder you are in, it can be very useful indeed!

Designing the solution

First, we're going to need a region form: launch the Add New Item dialog and select "ADX Region for Outlook and VSTO":

Next, the New Region Wizard will be displayed:

To create a region for the bottom of the Outlook Navigation Pane, select NavigationPane.Bottom in the Explorer Layout dropdown. Then check only MailItem in the Explorer Item Types listbox and click the Next button for the next screen of the Wizard:

The second step of the New Region Wizard is for designing Inspector (or form) Regions. We aren't using this region type in this demonstration, so we'll skip it; click the Next button.

The final screen of the New Region Wizard is where we can set some general properties for the region. Check "Always show header", uncheck "Allow the hidden state" and click the Finish button.

So what just happened? The wizard added a file based on the ADXOlForm class to the project. This is really a Windows Form that serves as a container for the region's custom UI, but it inherits from the AddinExpress.OL.ADXOlForm class instead of System.Windows.Forms.Form. All .NET controls can still be used on this form.

Also behind the scenes, a reference to the AddinExpress.Outlook.Regions.dll assembly was added to the VSTO project. This allows for the implementation of the Add-in Express Regions technology with all of the necessary Objects in the AddinExpress.Extensions and AddinExpress.OL namespaces.

The New Region Wizard also added a very important new component to the project: the FormsManager class:

This class exposes all of the necessary events for interacting with Regions:

  • ADXBeforeFolderSwitchEx
  • ADXBeforeFormInstanceCreate
  • ADXFolderSwitch
  • ADXFolderSwitchEx
  • ADXNavigationPaneHide
  • ADXNavigationPaneMinimize
  • ADXNavigationPaneShow
  • ADXNewInspector
  • ADXReadingPaneHide
  • ADXReadingPaneMove
  • ADXReadingPaneShow
  • ADXTodoBarHide
  • ADXTodoBarMinimize
  • ADXTodoBarShow
  • OnError
  • OnInitialize

Finally, the New Region Wizard added three lines to the project's ThisAddin class to initialize and finalize the regions:

VB.NET


					Public Class ThisAddIn
						Private Sub ThisAddIn_Startup() Handles Me.Startup
							' <auto-generated>
							' Add-in Express Regions generated code - do not modify
							Me.FormsManager = AddinExpress.OL.ADXOlFormsManager.CurrentInstance
							Me.FormsManager.Initialize(Me)
							' </auto-generated>
						End Sub

						Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown
							' <auto-generated>
							' Add-in Express Regions generated code - do not modify
							Me.FormsManager.Finalize(Me)
							' </auto-generated>
						End Sub
					End Class
					

Building the Outlook Navigation Pane region

Although the New Region Wizard automatically added most of the code we need for our new region form into the FormsManager class, we do need to make one small change regarding the caching strategy for the region. By default, new instances of view region forms are created every time the user switches to a folder that matches the context for the region. That is, since we selected MailItem for the Explorer Item types in the New Region Wizard, then the region will be displayed for all Outlook mail folders.

However, the Latest Mail form is intended to remain static for all mail folders as we need to maintain the same list of items no matter which folder is current. To ensure this happens, we need to set the Cached property for the region to ADXOlCachingStrategy.OneInstanceForAllFolders. This way a new instance of the form (with a blank list) is not created when the user navigates to a different mail folder.

VB.NET


					
					Private Sub FormsManager_OnInitialize() Handles FormsManager.OnInitialize
					
						'ADXOlForm1
						' TODO: Use the ADXOlForm3Item properties to configure the region's location,
						' appearance and behavior.
						' See the "The UI Mechanics" chapter of the Add-in Express Developer's Guide
						' for more information.
						Dim ADXOlForm1Item As ADXOlFormsCollectionItem = New ADXOlFormsCollectionItem()
						ADXOlForm1Item.ExplorerLayout = ADXOlExplorerLayout.BottomNavigationPane
						ADXOlForm1Item.ExplorerItemTypes = ADXOlExplorerItemTypes.olMailItem
						ADXOlForm1Item.AlwaysShowHeader = True
						ADXOlForm1Item.IsHiddenStateAllowed = False
						'Must change caching strategy so that the form retains its state for every folder
						ADXOlForm1Item.Cached = ADXOlCachingStrategy.OneInstanceForAllFolders
						ADXOlForm1Item.UseOfficeThemeForBackground = True
						ADXOlForm1Item.FormClassName = GetType(ADXOlForm1).FullName
						Me.FormsManager.Items.Add(ADXOlForm1Item)

					End Sub
					

Next, we need to design the UI. Simply add the following controls to ADXOlForm1:

  • Label ("Label1")
  • ListView ("ListView1")
  • ImageList ("ImageList1") - optional if you don't want to use an icon for each item in the list

Then add the following code to the ADXOlForm1 class:

VB.NET


					
					Private Sub ListView1_DoubleClick(ByVal sender As Object, _
							ByVal e As System.EventArgs) Handles ListView1.DoubleClick

							Dim myListViewItem As ListViewItem = ListView1.SelectedItems.Item(0)
							Dim myListViewSubItem As ListViewItem.ListViewSubItem = _
								myListViewItem.SubItems.Item(1)
							Dim myItem As MailItem

							Try
								myItem = Globals.ThisAddIn.m_OLNameSpace.GetItemFromID(myListViewSubItem.Text)
							Catch ex As System.Exception
								'Item may have been deleted or moved: The message you specified cannot be found. 
								MsgBox("The message is no longer available.", _
									MessageBoxButtons.OK, "Unknown Error")
								Exit Sub
							End Try

							If Not myItem Is Nothing Then
								Try
									myItem.Display()
								Catch ex As System.Exception
									' Item may have been deleted or moved: The message you specified
									' cannot be found.
									MsgBox("The message is no longer available.", _
										MessageBoxButtons.OK, "Unknown Error")
								End Try

								Marshal.ReleaseComObject(myItem)
								myItem = Nothing
							End If
						End Sub

						Private Sub ADXOlForm1_Load(ByVal sender As Object, _
							ByVal e As System.EventArgs) Handles Me.Load
							
							'The region is loading for the first time in the current Explorer
							If Globals.ThisAddIn.LatestMessages Is Nothing Then
								Exit Sub
							End If

							Dim myListViewItem As System.Windows.Forms.ListViewItem

							'Load an existing list of messages from the property stored in ThisAddIn
							For Each myListViewItem In Globals.ThisAddIn.LatestMessages
								Try
									Dim myNewItem As ListViewItem
									myNewItem = ListView1.Items.Add(myListViewItem.Clone)
								Catch ex As System.Exception
									System.Windows.Forms.MessageBox.Show(ex.ToString)
								End Try

							Next
						End Sub

					

The code allows for opening the e-mail when a selection in the ListView control is double-clicked, as well as reloading the list of Latest Messages if the user launches a new Explorer window. The list of Latest Messages is actually going to be generated from the ThisAddin class, where we'll demonstrate how to access ADXOlForm1.

Below is the code that will handle incoming e-mails and populating the list on ADXOlForm1. In summary, this is what the code does:

  • Creates Outlook Application and Namespace objects when the add-in loads
  • Implements the Application.NewMailEx event, which will fire every time one e-mail is received
  • Ignore non e-mails (e.g. Meeting requests, etc.)
  • Highlight the region header so that it flashes and catches the user's attention when a new Outlook e-mail is received

VB.NET


					
					Imports System.Runtime.InteropServices
					Imports AddinExpress.OL
					Imports System.Windows.Forms

					Public Class ThisAddIn
						Public m_OLNameSpace As Outlook.NameSpace
						Private WithEvents m_OutlookApp As Outlook.Application
						Private m_LatestMailRegion As ADXOlFormsCollectionItem
						Public Property LatestMessages() As ListView.ListViewItemCollection

						Private Sub ThisAddIn_Startup() Handles Me.Startup

							' <auto-generated>
							' Add-in Express Regions generated code - do not modify
							Me.FormsManager = AddinExpress.OL.ADXOlFormsManager.CurrentInstance
							Me.FormsManager.Initialize(Me)
							' </auto-generated>

							m_OutlookApp = Me.Application
							m_OLNameSpace = Me.Application.GetNamespace("MAPI")
						End Sub

						Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown

							' <auto-generated>
							' Add-in Express Regions generated code - do not modify
							Me.FormsManager.Finalize(Me)
							' </auto-generated>

							Marshal.ReleaseComObject(m_OLNameSpace)
						End Sub

						Private Sub m_OutlookApp_NewMailEx(ByVal EntryIDCollection As String) _
							Handles m_OutlookApp.NewMailEx
							
							Dim objExplorers As Outlook.Explorers

							Try
								objExplorers = Application.Explorers
								If Not objExplorers Is Nothing Then
									If objExplorers.Count = 0 Then
										GoTo Leave
									End If
								Else
									Exit Sub
								End If
							Catch ex As System.Exception
								System.Windows.Forms.MessageBox.Show(ex.ToString)
								Exit Sub
							End Try

							Dim objNewItem As Object
							Dim strEntryIDs() As String

							'Get the list of EntryIDs passed to NewMailEx
							'NOTE: NewMailEx in Outlook 2007/2010 only processes ONE new message in this event.
							'For backwards compatibility with Outlook 2003, this code will loop through
							'all EntryIDs
							strEntryIDs = Split(EntryIDCollection, ",")

							For Each strID As String In strEntryIDs
								Try
									'Get an item Object for each EntryID in the collection
									objNewItem = m_OLNameSpace.GetItemFromID(strID)
									If Not objNewItem Is Nothing Then
										'Only handle e-mails
										If objNewItem.Class = Outlook.OlObjectClass.olMail Then
											Dim objMail As Outlook.MailItem = _
												TryCast(objNewItem, Outlook.MailItem)                        

											'Loop through all Explorers and add latest message to ListView
											'control on every region
											For intX As Integer = 1 To objExplorers.Count
												Dim objExpl As Outlook.Explorer = objExplorers.Item(intX)
												Dim myADXOlForm1 As ADXOlForm1 = Nothing
												Dim myADOlRegionForm As AddinExpress.OL.ADXOlForm
												Dim objExplrHWND As IntPtr

												objExplrHWND = FormsManager.GetOutlookWindowHandle(objExpl)

												'Get the active Region instance
												myADOlRegionForm = FindForm(objExplrHWND)

												If Not myADOlRegionForm Is Nothing Then
													'Convert the ADXOlForm form to our instance of it
													myADXOlForm1 = TryCast(myADOlRegionForm, ADXOlForm1)

													If Not myADXOlForm1 Is Nothing Then
														Dim myListViewItem As System.Windows.Forms.ListViewItem
														Dim myListViewSubItem As _
															Windows.Forms.ListViewItem.ListViewSubItem

														'Add the message's Subject to the ListView control
														myListViewItem = _
															myADXOlForm1.ListView1.Items.Add(objMail.Subject)
														myListViewItem.ToolTipText = objMail.Subject
														'Optional if an ImageList control is not being used
														myListViewItem.ImageKey = "Mail.ico"

														myListViewSubItem = myListViewItem.SubItems.Add(strID)
														'Store the last accessed list of items in a Public
														'property for retrieval when creating the region
														'in a new Explorer window (so the ListView can be
														'repopulated)
														LatestMessages = myADXOlForm1.ListView1.Items

														'Highlight the region to notify the user that new
														'mail has been received
														myADXOlForm1.Highlight()
													End If
												End If
												objExpl = Nothing
											Next

											Marshal.ReleaseComObject(objNewItem)
											objNewItem = Nothing
											objMail = Nothing
										End If
									End If
								Catch ex As System.Exception
									System.Windows.Forms.MessageBox.Show(ex.ToString)
								End Try
							Next

					Leave:
							Marshal.ReleaseComObject(objExplorers)
							objExplorers = Nothing
						End Sub

						Public Property LatestMailRegion As ADXOlFormsCollectionItem
							Get
								Return FormsManager.Items(0)
							End Get
							Set(ByVal value As ADXOlFormsCollectionItem)
								m_LatestMailRegion = value
							End Set
						End Property

						Public Function FindForm(ByVal CurrentOutlookWindowHandle As IntPtr) _
							As AddinExpress.OL.ADXOlForm

							For i As Integer = 0 To LatestMailRegion.FormInstanceCount - 1
								If LatestMailRegion.FormInstances(i).Visible AndAlso _
									CurrentOutlookWindowHandle = _
									LatestMailRegion.FormInstances(i).CurrentOutlookWindowHandle Then
									Return TryCast(LatestMailRegion.FormInstances(i), AddinExpress.OL.ADXOlForm)
								End If
							Next

							Return Nothing
						End Function
					End Class