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
|