How to share Outlook Views and changes between folders
Every now and again, Eugene comes up with a certain challenge and asks me whether I would like to give it a go. In most of these cases it does involve a lot of trial and error and even more head scratching : ) The one challenge I’ll describe in todays’ article involves creating an Outlook folder view and applying it to two or more folders.
This in itself is not very hard to do. However, what if the user adds or removes fields from the one folder via the View Settings dialog?
The folders will still share the same view, but one will either have more or fewer fields than the other. In this article we’ll build an Outlook add-in that tracks the changes in the folder views and synchs the view fields between the folders.
To keep things simple, we’ll have a “master” folder where its view will dictate what fields should be in the other folders.
As always, start by creating a new COM Add-in with Add-in Express for Office and .net.
Complete the wizard by selecting Visual C# as the programming language, Microsoft Office 2007 as the minimum supported Office version and Microsoft Outlook as the supported application.
Next, we’ll add an Explorer Ribbon Tab to set up the master and child folders, as well as buttons to create and apply our view to the folders and another one for synching field changes between the folders. I’ve also added a checkbox to be able to specify whether the view fields should automatically synch when the ExplorerFolderSwitch event is raised. My design looks like the following image:
When you click on the Master Folder, Watch Folder 1 and Watch Folder 2 buttons you will be prompted to select which folder to use for each. For this example we’ll choose three folders that contain Mail and Post Items. The code to do this will be similar for each folder:
private void masterFolderRibbonButton_OnClick( object sender, IRibbonControl control, bool pressed) { Outlook.NameSpace session = null; Outlook.MAPIFolder masterFolder = null; try { session = OutlookApp.GetNamespace("MAPI"); masterFolder = session.PickFolder(); if (masterFolder != null) { masterFolderPath = masterFolder.FolderPath; masterFolderRibbonButton.Caption = "Master Folder : " + masterFolderPath; Properties.Settings.Default.MasterFolderPath = masterFolderPath; Properties.Settings.Default.Save(); } } finally { if (masterFolder != null) Marshal.ReleaseComObject(masterFolder); if (session != null) Marshal.ReleaseComObject(session); } }
Once all the folders have been selected, the ribbon buttons’ captions will show the selected folder paths as illustrated in the image below:
Each folder will have the Compact Inbox view by default.
Clicking on the “Create & Set View” button will remove some fields as well as the sorting fields from the folder views and create a new simpler view with only 4 fields.
The code to create and apply the new view is as follows:
private void setViewRibbonButton_OnClick( object sender, IRibbonControl control, bool pressed) { Outlook.MAPIFolder masterFolder = null; Outlook.MAPIFolder folder1 = null; Outlook.MAPIFolder folder2 = null; Outlook.Views masterFolderViews = null; Outlook.Views folder1Views = null; Outlook.Views folder2Views = null; Outlook.View folder1View = null; Outlook.View folder2View = null; try { // Get the folders masterFolder = FolderHelper.GetFolder(masterFolderPath, this.OutlookApp); folder1 = FolderHelper.GetFolder(folder1Path, this.OutlookApp); folder2 = FolderHelper.GetFolder(folder2Path, this.OutlookApp); // Get the folders views masterFolderViews = masterFolder.Views; folder1Views = folder1.Views; folder2Views = folder2.Views; // Add & setup the Master View CreateView(masterFolderViews); // Apply new view to folders folder1View = folder1Views["My Master View"]; folder1View.Apply(); folder2View = folder2Views["My Master View"]; folder2View.Apply(); } finally { if (masterFolder != null) Marshal.ReleaseComObject(masterFolder); if (folder1 != null) Marshal.ReleaseComObject(folder1); if (folder2 != null) Marshal.ReleaseComObject(folder2); if (masterFolderViews != null) Marshal.ReleaseComObject(masterFolderViews); if (folder1Views != null) Marshal.ReleaseComObject(folder1Views); if (folder2Views != null) Marshal.ReleaseComObject(folder2Views); if (folder1View != null) Marshal.ReleaseComObject(folder1View); if (folder2View != null) Marshal.ReleaseComObject(folder2View); } }
The CreateView method adds the Message Class field and removes all the other fields we do not need for the view as well as removes all the sort fields. Its code looks like this:
private void CreateView(Outlook.Views masterFolderViews) { Outlook.ViewFields masterFolderViewFields = null; Outlook.OrderFields masterFolderSortFields = null; Outlook.TableView masterView = null; try { masterView = masterFolderViews.Add( "My Master View", Outlook.OlViewType.olTableView, Outlook.OlViewSaveOption.olViewSaveOptionAllFoldersOfType) as Outlook.TableView; masterFolderViewFields = masterView.ViewFields; masterFolderSortFields = masterView.SortFields; masterFolderViewFields.Add("Message Class"); //Importance masterFolderViewFields.Remove( "urn:schemas:httpmail:importance"); //Reminder masterFolderViewFields.Remove( "https://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/8503000b"); //Icon masterFolderViewFields.Remove( "https://schemas.microsoft.com/mapi/proptag/0x0fff0102"); //Flag Status masterFolderViewFields.Remove( "https://schemas.microsoft.com/mapi/proptag/0x10900003"); //Attachment masterFolderViewFields.Remove( "urn:schemas:httpmail:hasattachment"); //Received masterFolderViewFields.Remove( "urn:schemas:httpmail:datereceived"); //Categories masterFolderViewFields.Remove( "urn:schemas-microsoft-com:office:office#Keywords"); // Remove the Sorting masterFolderSortFields.RemoveAll(); // Save & Apply the Master view masterView.Save(); masterView.Apply(); } finally { if (masterView != null) Marshal.ReleaseComObject(masterView); if (masterFolderViewFields != null) Marshal.ReleaseComObject(masterFolderViewFields); if (masterFolderSortFields != null) Marshal.ReleaseComObject(masterFolderSortFields); } }
With our base view in place, we can go ahead and add another field to the master folder view by using the Field Chooser.
Drag the To field to the view.
Next, switch to one of the watch folders we’ve specified earlier and you’ll notice that it does not have the To field, although it shares the same view with the master folder.
Click the “Synch Folder View” button or check the “Sync Views on ExplorerFolderSwitch” checkbox and switch back to one of the watch folders. You should now see that it also has the To folder. Both the event handler for the button click and the ExplorerFolderSwitch events call the SynchFields method:
private void synchViewsRibbonButton_OnClick( object sender, IRibbonControl control, bool pressed) { SynchFields(); } private void adxOutlookEvents_ExplorerFolderSwitch(object sender, object explorer) { if (eventsRibbonCheckBox.Pressed) SynchFields(); }
The SynchFields method looks like:
private void SynchFields() { Outlook.MAPIFolder masterFolder = null; Outlook.Views masterFolderViews = null; Outlook.TableView masterFolderView = null; Outlook.ViewFields masterFolderFields = null; Outlook.MAPIFolder folder1 = null; Outlook.Views folder1Views = null; Outlook.TableView folder1View = null; Outlook.ViewFields folder1Fields = null; Outlook.MAPIFolder folder2 = null; Outlook.Views folder2Views = null; Outlook.TableView folder2View = null; Outlook.ViewFields folder2Fields = null; try { masterFolder = FolderHelper.GetFolder(masterFolderPath, this.OutlookApp); masterFolderViews = masterFolder.Views; masterFolderView = masterFolder.CurrentView as Outlook.TableView; masterFolderFields = masterFolderView.ViewFields; folder1 = FolderHelper.GetFolder(folder1Path, this.OutlookApp); folder1Views = folder1.Views; folder1View = folder1.CurrentView as Outlook.TableView; folder1Fields = folder1View.ViewFields; folder2 = FolderHelper.GetFolder(folder2Path, this.OutlookApp); folder2Views = folder2.Views; folder2View = folder2.CurrentView as Outlook.TableView; folder2Fields = folder2View.ViewFields; if (masterFolderFields != null && folder1Fields != null) { ProcessFolderView(masterFolderFields, folder1Fields); } if (masterFolderFields != null && folder2Fields != null) { ProcessFolderView(masterFolderFields, folder2Fields); } } finally { if (folder2Fields != null) Marshal.ReleaseComObject(folder2Fields); if (folder2View != null) Marshal.ReleaseComObject(folder2View); if (folder2Views != null) Marshal.ReleaseComObject(folder2Views); if (folder2 != null) Marshal.ReleaseComObject(folder2); if (folder1Fields != null) Marshal.ReleaseComObject(folder1Fields); if (folder1View != null) Marshal.ReleaseComObject(folder1View); if (folder1Views != null) Marshal.ReleaseComObject(folder1Views); if (folder1 != null) Marshal.ReleaseComObject(folder1); if (masterFolderFields != null) Marshal.ReleaseComObject(masterFolderFields); if (masterFolderView != null) Marshal.ReleaseComObject(masterFolderView); if (masterFolderViews != null) Marshal.ReleaseComObject(masterFolderViews); if (masterFolder != null) Marshal.ReleaseComObject(masterFolder); } }
The code above gets the current view of the folders and then loops through the view fields of each folder and checks whether it either has extra fields not specified in the master view or whether it is missing fields specified in the master view and then either removes or adds them.
The logic to remove or add view fields is handled by the ProcessFolderView method:
private void ProcessFolderView( Outlook.ViewFields masterFolderFields, Outlook.ViewFields childFolderFields) { Outlook.ViewField masterField = null; Outlook.ViewField foundField = null; Outlook.View childFolderView = null; Outlook.View masterFolderView = null; try { childFolderView = childFolderFields.Parent as Outlook.View; for (int i = 1; i < = masterFolderFields.Count; i++) { masterField = masterFolderFields[i]; foundField = childFolderFields[masterField.ViewXMLSchemaName]; if (foundField != null) { Marshal.ReleaseComObject(foundField); foundField = null; } if (masterField != null) { Marshal.ReleaseComObject(masterField); masterField = null; } } } catch (COMException ex) { if (ex.ErrorCode == -2147467259)// The field does not exist in the view { childFolderFields.Add(masterField.ViewXMLSchemaName); childFolderView.Save(); childFolderView.Apply(); } } finally { if (foundField != null) Marshal.ReleaseComObject(foundField); if (masterField != null) Marshal.ReleaseComObject(masterField); } try { masterFolderView = masterFolderFields.Parent as Outlook.View; for (int i = 1; i <= childFolderFields.Count; i++) { masterField = childFolderFields[i]; foundField = masterFolderFields[masterField.ViewXMLSchemaName]; if (foundField != null) { Marshal.ReleaseComObject(foundField); foundField = null; } if (masterField != null) { Marshal.ReleaseComObject(masterField); masterField = null; } } } catch (COMException ex) { if (ex.ErrorCode == -2147467259)// The field does not exist in the view { childFolderFields.Remove(masterField.ViewXMLSchemaName); childFolderView.Save(); childFolderView.Apply(); } } finally { if (foundField != null) Marshal.ReleaseComObject(foundField); if (masterField != null) Marshal.ReleaseComObject(masterField); } }
If all goes well, your folder views should stay in sync with the master folder’s view. This is a very basic implementation of keeping your folder views synchronized, it’s not perfect but it works. You can also use a simpler method by simply setting each child folder view XML property to the master folder’s view XML property value.
Thank you for reading. Until next time, keep coding!
P.S: I’ve created a Visual Studio Code snippet template to make it easier to add the code to release your COM object. It comes in very handy when you have a bunch of COM objects to release. To use it, add it to your Visual Studio snippets library and simply type relcom to insert the code. Download it here.
Available downloads:
The sample COM Add-in was developed using Add-in Express for Office and .net:
C# sample COM add-in
Release COM Object snippet