This chapter has already covered how to iterate over the Outlook items in a MAPIFolder by using foreach with the Items collection. This section examines some additional methods that you can use when working with the Items collection.
Iterating over Outlook Items
The Items collection's SetColumns method enables you to tell Outlook to cache certain properties when you iterate over the Items collection so that access to those properties will be fast. An Outlook item has a number of properties associated with itname value pairs that can be accessed by using an Outlook item's ItemProperties property. A typical MailItem has around 80 properties associated with it.
If you know that you are going to iterate using foreach over the Items collection and you are only going to be accessing the Subject and CreationTime properties of Outlook items in that collection, you can call the Items collection's SetColumns method before iterating the collection and pass the string "Subject, CreationTime". Some limitations apply to which properties can be cached (for example, properties which return objects cannot be cached)check the documentation before using this method. After you have iterated over the collection, use the Items collection's ResetColumns method to clear the cache of properties Outlook created.
The Items collection's Sort method enables you to apply a sort order to the Items collection before you iterate over the collection using foreach. The method takes a Property parameter as a string, which gives the name of the property by which to sort. You pass the name of the property enclosed in square brackets. To sort by subject you would pass "[Subject]". The Sort method also takes an optional Descending parameter that can be passed true to sort descending, false to sort ascending. The default value if you pass Type.Missing is false. Some limitations apply to which properties can sorted oncheck the documentation before using this method.
Listing 11-16 illustrates using the SetColumns and Sort methods. It times the operation of iterating through all the items in the Inbox and examining the Subject property without calling SetColumns. It then times the operation again but calls SetColumns first. Finally, Sort is illustrated, and the first item and last item in the sorted Items collection are accessed using the index operator. The Items collection's Count property is also used to get the index of the last item in the Items collection.
Listing 11-16. A VSTO Add-In That Uses the Items Collection's SetColumns and Sort Methods
private void ThisApplication_Startup(object sender, EventArgs e) { Outlook.MAPIFolder inbox = this.Session.GetDefaultFolder( Outlook.OlDefaultFolders.olFolderInbox); Outlook.Items myItems = inbox.Items; MessageBox.Show("Click OK to start the test."); System.DateTime start = System.DateTime.Now; foreach (object item in myItems) { string subject = (string)GetPropertyHelper(item, "Subject"); } System.DateTime end = System.DateTime.Now; System.TimeSpan result1 = end.Subtract(start); MessageBox.Show(String.Format( "Without calling SetColumns this took {0} ticks.", result1.Ticks)); start = System.DateTime.Now; myItems.SetColumns("Subject"); foreach (object item in myItems) { string subject = (string)GetPropertyHelper(item, "Subject"); } end = System.DateTime.Now; System.TimeSpan result2 = end.Subtract(start); MessageBox.Show(String.Format( "With SetColumns this took {0} ticks.", result2.Ticks)); myItems.ResetColumns(); myItems.Sort("[Subject]", missing); object firstItem = myItems[1]; object lastItem = myItems[myItems.Count]; MessageBox.Show(String.Format( "First item is {0}.", (string)GetPropertyHelper(firstItem, "Subject"))); MessageBox.Show(String.Format( "Last item is {0}." , (string)GetPropertyHelper(lastItem, "Subject"))); } private object GetPropertyHelper(object targetObject, string propertyName) { return targetObject.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty, null, targetObject, null, System.Globalization.CultureInfo.CurrentCulture); }
Finding an Outlook Item
The Items collection's Find method enables you to find an Outlook item in the Items collection by querying the value of one or more properties associated with the Outlook item. The Find method takes a string, which contains a filter to apply to find an Outlook item. For example, you might want to find an Outlook item in the items collection with its Subject property set to "RE: Payroll". The way you would call Find would look like this:
object foundItem = myItems.Find(@"[Subject] = ""RE: Payroll""");
The query string has the name of the property in brackets. We use C#'s literal string syntax (@) to specify a string with quotation marks surrounding the value we are searching the Subject field for. Alternatively, you could call Find substituting apostrophes for the quotation marks used in the first example:
object foundItem = myItems.Find(@"[Subject] = 'RE: Payroll'";
If the Items collection does not contain an Outlook item whose Subject property is equal to "RE: Payroll", the Find method returns null. If there are multiple Outlook items in the Items collection whose Subject property is equal to "RE: Payroll", you can continue finding additional items by using the Items collection's FindNext method. The FindNext method finds the next Outlook item in the collection that matches the filter string passed to Find. You can continue to call FindNext until FindNext returns null indicating that no more items could be found, as shown in Listing 11-17.
Listing 11-17. A VSTO Add-In That Uses the Items Collection's Find and FindNext Methods
private void ThisApplication_Startup(object sender, EventArgs e) { Outlook.MAPIFolder inbox = this.Session.GetDefaultFolder( Outlook.OlDefaultFolders.olFolderInbox); Outlook.Items myItems = inbox.Items; object foundItem = myItems.Find(@"[Subject] = ""RE: Payroll"""); while (foundItem != null) { MessageBox.Show(String.Format( "Found item with EntryID {0}.", (string)GetPropertyHelper(foundItem, "EntryID"))); foundItem = myItems.FindNext(); } } private object GetPropertyHelper(object targetObject, string propertyName) { return targetObject.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty, null, targetObject, null, System.Globalization.CultureInfo.CurrentCulture); }
We have illustrated a rather simple filter string that just checks to see whether a text property called Subject matches a string. It is possible to use the logical operators AND, OR, and NOT to specify multiple criteria. For example, the following filter strings check both the property Subject and the property CompanyName. The first finds an Outlook item where the Subject is "RE: Payroll" and the CompanyName is "Microsoft". The second finds an Outlook item where the Subject is "RE: Payroll" or the CompanyName is "Microsoft". The third finds an Outlook item where the Subject is "RE: Payroll" and the CompanyName is not "Microsoft".
[View full width]
object foundItem = myItems.Find(@"[Subject] = 'RE: Payroll' AND [CompanyName] = 'Microsoft'"); object foundItem = myItems.Find(@"[Subject] = 'RE: Payroll' OR [CompanyName] = 'Microsoft'"); object foundItem = myItems.Find(@"[Subject] = 'RE: Payroll' AND NOT [CompanyName] = 'Microsoft'");
When searching for a property that is an integer value, it is not necessary to enclose the integer value you are searching for in quotes. The same is true for a property that is a boolean property. This example searches for an Outlook item whose integer property OutlookInternalVersion is equal to 116359 and whose boolean property NoAging is set to False.
object foundItem = myItems.Find(@"[OutlookInternalVersion] = 116359 AND [NoAging] = False";
Some limitations apply to which properties you can use in a filter string. For example, properties that return objects cannot be examined in a filter string. Check the documentation of the Outlook object model for more information.
If you are working with an Items collection that has a large number of Outlook items in it, consider using the Items collection's Restrict method rather than Find and FindNext. The Restrict method is used in a similar way to how SetColumns and Sort are used. You call the Restrict method on the Items collection passing the same kind of filter string you provide to the Find method. You then can use foreach to iterate over the Items collection, and only the Outlook items that match the filter string will be iterated over. The Restrict method can be faster than Find and FindNext if you have a large number of items in the Items collection and you only expect to find a few items. Listing 11-18 illustrates using the Restrict method.
Listing 11-18. A VSTO Add-In That Uses the Items Collection's Restrict Method
private void ThisApplication_Startup(object sender, EventArgs e) { Outlook.MAPIFolder inbox = this.Session.GetDefaultFolder( Outlook.OlDefaultFolders.olFolderInbox); Outlook.Items myItems = inbox.Items; myItems.Restrict(@"[Subject] = ""RE: Payroll"""); foreach (object foundItem in myItems) { MessageBox.Show(String.Format( "Found item with EntryID {0}.", (string)GetPropertyHelper(foundItem, "EntryID"))); } } private object GetPropertyHelper(object targetObject, string propertyName) { return targetObject.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty, null, targetObject, null, System.Globalization.CultureInfo.CurrentCulture); }
Adding an Outlook Item to an Items Collection
To add a new Outlook Item to an Items collection, use the Items collection's Add method. The Add method takes an optional Type parameter of type object to which you can pass a member of the OlItemType enumeration: olAppointmentItem, olContactItem, olDistributionListItem, olJournalItem, olMailItem, olNoteItem, olPostItem, or olTaskItem. If you omit the Type parameter by passing Type.Missing, the type of the item is determined by the type of folder (as determined by DefaultItemType) that you are adding the item to. The Add method returns an object, which can be cast to the Outlook item type corresponding to the Type parameter that was passed in.
You must remember that you can only add an Outlook item that is compatible with the folder type the Items collection came fromfor example, it is not possible to add a ContactItem to an Items collection from a folder that is designated to hold MailItems and PostItems. For more information on the Outlook item types that can be contained by a particular folder type, see Table 11-6.
Listing 11-19 shows an example of using the Add method to add a PostItem and a MailItem to the Inbox folder. Note that using the Add method is not sufficient to get the PostItem and MailItem added to the Inbox folder. For the PostItem, we also have to call the Save method on the newly created Outlook item; otherwise, Outlook discards the PostItem when the variable postItem that refers to it goes out of scope. We also have to call Save on the newly created MailItem. In addition, we have to call the Move method to move the newly created MailItem into the Inbox folder. This is necessary because Outlook puts newly created MailItems into the Drafts folder by defaulteven though we called Add on the Items collection associated with the Inbox. Without the call to Move, the newly created MailItem remains in the Drafts folder.
Listing 11-19. A VSTO Add-In That Adds a MailItem and a PostItem
private void ThisApplication_Startup(object sender, EventArgs e) { Outlook.MAPIFolder inbox = this.Session.GetDefaultFolder( Outlook.OlDefaultFolders.olFolderInbox); Outlook.Items myItems = inbox.Items; Outlook.PostItem postItem = myItems.Add( Outlook.OlItemType.olPostItem) as Outlook.PostItem; postItem.Subject = "Test1"; postItem.Save(); Outlook.MailItem mailItem = myItems.Add( Outlook.OlItemType.olMailItem) as Outlook.MailItem; mailItem.Subject = "Test2"; mailItem.Save(); mailItem.Move(inbox); }
An alternate way to create an Outlook item is to use the Application object's CreateItem method. This method takes a Type parameter of type OlItemType that is passed a member of the OlItemType enumeration. It returns an object representing the newly created Outlook item. You must then save the created item and place it in the folder you want to store it in. Listing 11-20 shows code that uses CreateItem to do the same thing that Listing 11-19 does. In Listing 11-20, we must move the new MailItem and PostItem to the Inbox folder using the Move method on MailItem and PostItem.
Listing 11-20. A VSTO Add-In That Uses the Application Object's CreateItem Method to Add a MailItem and a PostItem
private void ThisApplication_Startup(object sender, EventArgs e) { Outlook.MAPIFolder inbox = this.Session.GetDefaultFolder( Outlook.OlDefaultFolders.olFolderInbox); Outlook.MailItem mailItem = this.CreateItem( Outlook.OlItemType.olMailItem) as Outlook.MailItem; mailItem.Subject = "Test 1"; mailItem.Save(); mailItem.Move(inbox); Outlook.PostItem postItem = this.CreateItem( Outlook.OlItemType.olPostItem) as Outlook.PostItem; postItem.Subject = "Test 2"; postItem.Save(); postItem.Move(inbox); }
Part One. An Introduction to VSTO
An Introduction to Office Programming
Introduction to Office Solutions
Part Two. Office Programming in .NET
Programming Excel
Working with Excel Events
Working with Excel Objects
Programming Word
Working with Word Events
Working with Word Objects
Programming Outlook
Working with Outlook Events
Working with Outlook Objects
Introduction to InfoPath
Part Three. Office Programming in VSTO
The VSTO Programming Model
Using Windows Forms in VSTO
Working with Actions Pane
Working with Smart Tags in VSTO
VSTO Data Programming
Server Data Scenarios
.NET Code Security
Deployment
Part Four. Advanced Office Programming
Working with XML in Excel
Working with XML in Word
Developing COM Add-Ins for Word and Excel
Creating Outlook Add-Ins with VSTO