Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.
Sue Mosher
Slipstick Systems Outlook & Exchange Solutions
December 2002
Applies to:
Microsoft® Outlook® 2002
Summary: This article is adapted from the book Microsoft Outlook Programming: Jumpstart for Administrators, Developers, and Power Users by Sue Mosher – ISBN - 55558-286-9, Digital Press, 2002, pages 191 to 217. (25 printed pages)
11.1 Application object events
11.2 Writing handlers for other object events
11.3 Explorer events
11.4 Inspector events
11.5 Folders and Items events
11.6 Reminders Events
11.7 Other Events
11.8 Summary
Before Microsoft® Outlook® 2000, the only way to respond to events in Outlook was through VBScript code on a form. Programmers were limited to just the few item events. Outlook 2000 opened the door to programming event handlers at the application level with the more powerful VBA language. Although not every possible event is included in the Outlook object model, the range of available events is enough to keep any programmer busy for a long, long time. Here are just a few of the events for which you can write code:
- Starting Outlook
- Sending an item
- Receiving new mail
- Creating or modifying items or folders
- Switching to a different folder or to a different view
Highlights of this chapter include discussions of the following:
- What event code to place in the
ThisOutlookSession
module - How to set up folders for monitoring new and changed items
- How to automatically add reminders to birthdays and anniversaries
If you skipped ahead to this chapter, you might want to make sure you understand the material in Part II, "Adding VBA Code," since this chapter requires a good understanding of basic coding techniques.
The Application
object stands at the top of the object model hierarchy and offers events that are useful to any Outlook 2000 or 2002 programmer, plus one (OptionsPagesAdd
) of interest mainly to developers building COM add-ins (which is beyond the scope of this book). Table 11.1 lists the key events. Only ItemSend
can be cancelled. The arguments for both ItemSend
and Reminder
include the associated item so that you work with it in your code.
Table 11.1 Key Application object events
**Note **Two other Application events,
AdvancedSearchComplete
and
AdvancedSearchStopped
, work with the
Search
object added in Outlook 2002 to make it possible to perform more precise data searches, in some case across multiple folders. You will see how to build these searches in Chapter 14, "Working with Items and Recipients."
In the object browser, you can see the events for various Outlook objects more easily if you right-click in the Members pane on the right and choose Group Members. Build code for any of these events in the ThisOutlookSession
module found in the Project Explorer under Project1 and then Microsoft Outlook Objects, as shown in Figure 11.1. Double-click ThisOutlookSession
to open it in a module window. This is a special kind of module, called a class module, that can respond to events. We'll come back to class modules when we consider handlers for events for objects other than the Application
object.
Figure 11.1 Place application-level event code in the ThisOutlookSession class module (click picture for larger image).
Since the ThisOutlookSession
module was created automatically, it will not include an Option Explicit
statement to force you to declare variables. You should go ahead and add that to the module's declarations section.
To add an Application
event handler, select Application
from the list at the top left of the module window. Then, from the list on the right, select the event for which you want to write code. VBA places a wrapper for the procedure in the module window, with the correct syntax. Figure 11.1 depicts a wrapper for the ItemSend
and Startup
events.
One use for the Startup event and, in Outlook 2002, MAPILogonComplete
, is to initialize global variables. For example, in any module (a regular module, not ThisOutlookSession
or another class module) besides, add this declaration:
Public g_strUser as String
Then use the code in Listing 11.1 to initialize this variable when Outlooks starts. That way, you can use it in any VBA procedure. (See Listing 10.10 for the code for the WSHUserName()
function.)
Listing 11.1 Initialize and dereference key object variables
Private Sub Application_Startup()
g_strUser = WSHUserName()
End Sub
As you will see later in the chapter, another important use of the Startup
event is to instantiate other Outlook objects that you plan to write event handlers for. In Outlook 2002, you can also use the MAPILogonComplete
event instead of Startup
, especially if you are using a profile that connects to Microsoft Exchange Server.
The Quit
event is not very useful, because all Outlook windows have already closed by the time the Quit
event fires as you exit Outlook. Therefore, you no longer have access to Outlook items and folders. Also, by the time Quit
fires, Outlook has already released any global variables.
Outlook offers several built-in options for notifying the user that new mail has arrived, but maybe you want something more customized. Try creating a VBA form, such as that in Figure 11.2, which pops up when new mail arrives and displays the time of the latest mail delivery. If you're often out of your office during the day, this form will make it easy to see at a glance whether any new messages arrived while you were gone—and at what time.
Figure 11.2 This VBA form is displayed whenever the NewMail event fires.
The form in Figure 11.2 has just two label controls. The one to hold the date and time information is named lblReceived
. Name the form Ch11NewMail
and set its ShowModal
property to False
. Add a command button named cmdHide
, and add this code to the form:
Private Sub cmdHide_Click()
Me.Hide
End Sub
To make the form display the most recent mail delivery time, add the following code to the Application_NewMail
event in the ThisOutlookSession
module:
Private Sub Application_NewMail()
Ch11NewMail.Show
With Ch11NewMail
.lblReceived.Caption = Now
.Repaint
End With
End Sub
The new mail notification options that you can set through the Outlook user interface do not fire on every incoming message. This applies to both the Rules Wizard and code that you supply to the NewMail
event handler. We will look at another way of processing new mail by monitoring the Inbox, later in this chapter. If you set ShowModal
property to False
, you can leave the form on the screen while you do other work. Click the Hide button to make the form disappear until the next new mail comes in.
Not everyone likes to be reminded of tasks and appointments with a pop-up message. Some people prefer to see reminders as items in their Inbox. Because the Reminder
event gives you access to the item that triggered the reminder, you can place a message in your own Inbox. The code in Listing 11.2 creates a new message from the reminder item and then places the new message in the Inbox. Place the code in the ThisOutlookSession
module.
Listing 11.2 Placing Reminders in the Inbox
Private Sub Application_Reminder(ByVal Item As Object)
Dim objNS As Outlook.NameSpace
Dim objItem As Outlook.MailItem
Dim objFolder As Outlook.MAPIFolder
Dim strDue As String
Dim objCDOItem As MAPI.Message
Set objNS = Application.GetNamespace("MAPI")
Set objItem = Application.CreateItem(olMailItem)
Select Case Item.Class
Case olMail
strDue = " Due " & Item.FlagDueBy
Case olAppointment
If Item.Location <> "" Then
strDue = " (" & Item.Location & ")"
End If
strDue = strDue & " At " & Item.Start
Case olContact
Set objCDOItem = GetCDOItemFromOL(Item)
If Not objCDOItem Is Nothing Then
strDue = " Due " & _
objCDOItem.Fields(CdoPR_REPLY_TIME)
End If
Case olTask
strDue = " Due " & Item.DueDate
End Select
If Item.Importance = olImportanceHigh Then
strDue = strDue & " (High)"
End If
With objItem
.Subject = Replace(TypeName(Item), "Item", "") & _
": " & Item.Subject & strDue
.Body = Item.Body
.Save
Set objFolder = _
objNS.GetDefaultFolder(olFolderInbox)
.Move objFolder
End With
Set objNS = Nothing
Set objItem = Nothing
Set objFolder = Nothing
Set objCDOItem = Nothing
End Sub
Because the Application_Reminder
subroutine is in the ThisOutlookSession
module, Application
is an intrinsic object; you do not have to declare an object variable for it.
If you look in the object browser, you'll see that the ContactItem
has no FlagDueBy
property like the MailItem
does. The due date is, however, stored in the item in a field that CDO can access, so the code passes the item to CDO, using the GetCDOItemFromOL()
function from Listing 10.5 and obtains the value for the flag's due date with the expression. objCDOItem.Fields(CdoPR_REPLY_TIME)
. The CdoPR_REPLY_TIME
constant represents the due date for the flag set on the contact. Appendix A has information on resources for learning more about CDO fields and how they related to Outlook items.
TypeName()
is a function that returns a string with the type of object, for example, "ContactItem" for an Outlook contact. Outlook automatically saves unsent messages in the Drafts folder. That's why the code uses the Move
method to get the reminder notice into the Inbox. Moving an item to the Inbox does not trigger the NewMail
event.There are lots of variations on this technique for processing reminders. For example, you could substitute this code for the With...End With
block to put a shortcut to the original item into the Inbox message:
objItem.Subject = "Reminder - " & Item.Subject & strDue
objItem.Body = Item.Body
Set objAttachment = _
objItem.Attachments.Add(Item, olEmbeddeditem)
objItem.Save
objItem.Move objFolder
Only items in your default Outlook Inbox, Calendar, Contacts, and Tasks folders will trigger the Application.Reminder
event.
Another possible application for the Reminder
event is to forward reminders to another e-mail account or perhaps even to an email address for a mobile phone. However, as you will see in Chapter 13, "Understanding Outlook Security," many Outlook installations cannot send items automatically without user intervention.
When the ItemSend
event fires, Outlook has not yet sent the item. This means that you can use the ItemSend
event to change the item before it leaves your Outbox.
Some mail applications, such as Lotus Notes, can prompt the sender to specify what folder a message should be saved in. In Outlook, you can set the storage folder for an individual message on the message's Options dialog. If you want to approximate the way Notes works, you can use the code in Listing 11.3 to pop up a dialog box when the user sends the message and then set the SaveSentMessageFolder
property to whatever folder the user chooses. Place the code in the ThisOutlookSession
module.
Listing 11.3 Setting the folder for saving an outgoing message
Private Sub Application_ItemSend(ByVal Item As Object, _
Cancel As Boolean)
Dim objNS As Outlook.NameSpace
Dim objFolder As Outlook.MAPIFolder
Set objNS = Application.GetNamespace("MAPI")
Set objFolder = objNS.PickFolder
If Not objFolder Is Nothing Then
If IsInDefaultStore(objFolder) Then
Set Item.SaveSentMessageFolder = objFolder
End If
End If
Set objFolder = Nothing
Set objNS = Nothing
End Sub
Since you are working in the ThisOutlookSession
module you can use the same Application
object that fires the ItemSend
event. The PickFolder
method of the Namespace
object is one of several techniques for getting a particular MAPIFolder
object that we'll cover in Chapter 12, "Working with Stores and Folders." Did you notice that setting the SaveSentMessageFolder
property required the Set keyword, because it's an object property? Listing 11.3 uses an IsInDefaultStore()
function, which you will see in Chapter 12, to comply with Outlook's requirement that the SaveSentMessageFolder
be in the same information store as the Sent Items folder. Another common use for the ItemSend
event is to check for conditions under which you might want to cancel sending the item—such as a message that says it contains an attachment, but doesn't. To cancel the sending of an item in the ItemSend
event handler, add this statement to your code:
Cancel = True
For example, Outlook 2002 introduces a new ShowCategoriesDialog
method that pops up the Categories dialog, where the user can choose to apply one or more categories to an item. If you want to make sure that all outgoing items have a category, you could use the code in listing 11.4.
Listing 11.4 Require a category on all outgoing items
Private Sub Application_ItemSend(ByVal Item As Object, _
Cancel As Boolean)
If Item.Categories = "" Then
Item.ShowCategoriesDialog
If Item.Categories = "" Then
Cancel = True
MsgBox "This item can't be sent " & _
"until you choose a category."
End If
End If
End Sub
Be careful with the technique in Listing 11.4, since the categories you choose will be visible to recipients who also have Outlook. Don't use category names that you might find embarrassing.
VBA handling of Outlook events is not limited to events associated with the Application
object. You can write event handlers for other Outlook objects, too. Setting this up is a little more involved because you must first declare object variables using a Dim WithEvents
statement. WithEvents
can be used only in class modules—special code modules that establish and work with object classes and their methods, events, and properties. The ThisOutlookSession
module itself is a class module.VBA forms also have associated class modules. The code placed behind forms like the birthday/anniversary reminder form that you worked on in earlier chapters is actually code in a class module. You can also declare WithEvents
statements in VBA form code.Follow this basic procedure to set up an event handler for any Outlook event other than Application
events:
- Declare an object variable
WithEvents
in theThisOutlookSession
module or in a class module that you add with the Insert, Class Module command in the VBA environment. - Initialize the declared object with a statement in either the
Application_Startup
procedure, if you always want it to run, or some other procedure, if you want to run it only on demand or in certain situations. - Write code in the
ThisOutlookSession
module to respond to the declared object's events.The individual Outlook items –MailItem
,ContactItem
, etc. – all have their own set of events. However, handling events for Outlook items in VBA is beyond the scope of this book, because it involves a fairly complex class module and detection of each item that the user selects in a folder or opens in its own window. We will deal with events for Outlook items solely in the context of Outlook forms, in Chapter 18, "Writing Code to Respond to Outlook Form Events."
You should remember by now that each window with an open Outlook folder is represented by an Explorer
object in the Explorers
collection. Events related to the Explorer
object fire when the user changes views, selects items, or switches to a new folder. Table 11.2 summarizes the Explorer
events.
Table 11.2 Explorer
events
Event | Description |
---|---|
Activate |
Occurs when the user switches to the Explorer window. |
BeforeFolderSwitch |
Occurs just before the Explorer displays a new folder. Includes the new folder as an argument. Cancelable. |
BeforeViewSwitch |
Occurs just before the Explorer displays a new view. Includes the new view as an argument. Cancelable. |
Close |
Occurs when the Explorer closes. |
Deactivate |
Occurs just before the focus switches from the Explorer to another window. |
FolderSwitch |
Occurs just after the Explorer displays a new folder. |
SelectionChange |
Occurs when the user selects different items. Does not apply to Outlook Today or file system folders. |
ViewSwitch |
Occurs after the Explorer displays a new view. |
Added in Outlook 2002 | |
BeforeItemCopy |
Occurs when the user copies an item. Cancelable. |
BeforeItemCut |
Occurs when the user cuts an item. Cancelable. |
BeforeItemPaste |
Occurs when the user pastes an item. Cancelable. |
BeforeMaximize |
Occurs when the user maximizes the window. Cancelable. |
BeforeMinimize |
Occurs when the user minimizes the window. Cancelable. |
BeforeMove |
Occurs when the user moves the window. Cancelable. |
BeforeSize |
Occurs when the user resizes the window. Cancelable. |
The BeforeFolderSwitch
, BeforeViewSwitch
, FolderSwitch
, and ViewSwitch
events can be triggered either by the user changing the folder or view or by code that assigns a new value to the Explorer
's CurrentFolder
or CurrentView
property.
To make use of these events, you must declare appropriate object variables in the ThisOutlookSession
module or another class module and should initialize those variables with code in the Application_Startup
event handler in ThisOutlookSession
. If you want to detect when a user has opened a new window and then change the appearance of that window, SelectionChange
is the best event to use, because it ensures that the full user interface is available.
You might have discovered that you can open multiple Outlook windows by right-clicking on the name of any folder, then choosing Open in New Window
. However, these windows do not show the Outlook Bar. To show how you can use the SelectionChange
event to work with the appearance of an Explorer window, we will create the Explorer
event handlers in a separate class module. This will also give you a little practice working with class modules and make it easier to back up your event handler module by exporting the class module from VBA. You can export the ThisOutlookSession
module, but when you try to import it, it doesn't replace or update the existing ThisOutlookSession
module. Instead, it imports as ThisOutlookSession1
. You would then have to copy and paste the code from ThisOutlookSession1
to ThisOutlookSession
. Follow these step-by-step instructions to create your class module and update ThisOutlookSession
with the necessary code:
- Choose Insert, Class Module to create a new class module in VBA.
- In the Properties window, change the value of the
(Name)
property toExplEvents
. - Add the code in Listing 11.5 to the
ExplEvents
module. - Edit the
ThisOutlookSession
module to include the code in Listing 11.6. If you already have anApplication_Startup
procedure, do not create a new one. Simply add the code from the procedure in Listing 11.6 to your existing routine. - Either exit and restart Outlook, or run the
Application_Startup
procedure to initialize the new event handlers.
Listing 11.5 Class module code to handle Explorer and Explorers events
Private WithEvents m_colExplorers As Outlook.Explorers
Private WithEvents m_objExplorer As Outlook.ExplorerPrivate Sub Class_Terminate()
Call DeRefExplorers
End Sub
Public Sub InitExplorers(objApp As Outlook.Application)
Set m_colExplorers = objApp.Explorers
If m_colExplorers.Count > 0 Then
Set m_objExplorer = objApp.ActiveExplorer
End If
End Sub
Public Sub DeRefExplorers()
Set m_colExplorers = Nothing
Set m_objExplorer = Nothing
End Sub
Private Sub m_colExplorers_NewExplorer _
(ByVal Explorer As Explorer)
Set m_objExplorer = Explorer
End Sub
Private Sub m_objExplorer_SelectionChange()
If Not m_objExplorer.IsPaneVisible(olOutlookBar) Then
m_objExplorer.ShowPane olOutlookBar, True
End If
End Sub
Listing 11.6 ThisOutlookSession
code to handle an Explorers
collection
Dim m_explevents As New ExplEventsPrivate Sub Application_Startup()
m_explevents.InitExplorers Application
End Sub
It is possible to start Outlook without first displaying a folder—for example, by using a shortcut to display a new message window. Therefore, the InitExplorers
procedure only instantiates the m_objExplorer
if it can confirm that at least one Explorer object (that is, Outlook folder window) is available.
Listing 11.6 introduces a new declaration technique—the New
keyword. When you use the New
keyword in a declaration, you are creating a new instance of a class, in this case the class module that you named ExplEvents
. We need to create an instance of the class before we can call any of the procedures in that class module, for example the m_events.InitExplorers
procedure called in Application_Startup
. Explorer
events allow you to gain sure access only to the last Explorer window opened. Handling all events for all open Explorer windows requires a "wrapper" class module and is beyond the scope of this book. Appendix A has further resources. Other practical applications for Explorer
events include the following:
- Turning off the preview pane when you switch to a view with "AutoPreview" in its name, so that you don't have two different kinds of preview in a single view
- Turning on a custom toolbar when you switch to a particular folder and turning it off again when you switch to a different folder
- Automatically showing a certain view when you switch to a folder, rather than showing the last view used on that folder
If you're like me and have several thousand items in your Sent Items folder, viewing just the last few days' worth makes the folder seem to run faster. Outlook includes a Last Seven Days view that filters out all but the last week's worth of items.
**Note **You may want to create your own Sent in Last Seven Days view, replacing the Received and From fields with the Sent and To fields.
To make Outlook automatically turn on the Last Seven Days view, you must create an event handler for the FolderSwitch
event. Add the code in Listing 11.7 to your ExplEvents class module.
This code depends on having an m_objExplorer
object declared WithEvents
and initialized, as described in the previous section.
Listing 11.7 Enforcing a Default Folder View
Private Sub m_objExplorer_FolderSwitch()
Dim objApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Dim objSentItems As Outlook.MAPIFolder
Set objApp = CreateObject("Outlook.Application")
Set objNS = objApp.GetNamespace("MAPI")
Set objSentItems = _
objNS.GetDefaultFolder(olFolderSentMail)
If m_objExplorer.CurrentFolder = objSentItems Then
m_objExplorer.CurrentView = "Last Seven Days"
End If
Set objApp = Nothing
Set objNS = Nothing
Set objSentItems = Nothing
End Sub
Just as Outlook has an Explorers
collection with each individual Explorer
object representing a folder window, it also has an Inspectors
collection, where each individual Inspector
object represents an individual Outlook item window. The Inspectors
collection has one event, NewInspector
, which fires whenever a new Inspector
opens. Unfortunately, the NewInspector
event does not fire in all cases where the user opens a new Outlook message. In Outlook 2000, it does not fire when Word is used as the editor (a configuration known as WordMail). In both Outlook 2000 and 2002, you will get no NewInspector
event when you invoke a Send
or Send To
command from other Office programs, Windows Explorer, or Internet Explorer. Furthermore, using the Next or Previous buttons in an open item window reuses the corresponding Inspector
, so you don't get a NewInspector
event there either, even though the item being viewed changes.
Further complicating the picture is the fact that, like NewExplorer
, the NewInspector
event provides only the most recently opened Inspector
. To handle events for all open Outlook Inspector windows would require a "wrapper" class module, which is beyond the scope of this book.
Perhaps the most practical use of the NewInspector
event is to make sure that a particular custom toolbar or toolbar button is visible.
An individual Inspector
object has the events shown in Table 11.3, including several that apply only to Outlook 2002.
Table 11.3 Inspectors and Inspector events
Event | Description |
---|---|
Inspectors event
|
|
NewInspector
|
Occurs when an item opens in its own window (but not for Send or SendTo commands from Office programs, Windows Explorer, or Internet Explorer or in WordMail in Outlook 2000). |
Inspector Events
|
|
Activate
|
Occurs when the user switches to the Inspector window or when the Next or Previous button is used to view another item in an open window. |
Close
|
Occurs when the Inspector closes. |
Deactivate
|
Occurs just before the focus switches from the Inspector to another window or when the Next or Previous button is used to view another item in an open window. |
Added in Outlook 2002 | |
BeforeMaximize
|
Occurs when the user maximizes the window. Cancelable. |
BeforeMinimize
|
Occurs when the user minimizes the window. Cancelable. |
BeforeMove
|
Occurs when the user moves the window. Cancelable. |
BeforeSize
|
Occurs when the user resizes the window. Cancelable. |
Another major category of events is those that affect the Folders
and Items
collections—in other words, Outlook folders and the items they contain. This is where Outlook reacts to the creation of a new folder or item, a change to an existing folder or item, or the deletion of a folder or item. Table 11.4 summarizes these events.
Table 11.4 Folders and Items Events
Event | Description |
---|---|
Folders Events
|
|
FolderAdd
|
Occurs when a new folder is created. Includes the new MAPIFolder as an argument. |
FolderChange
|
Occurs when a folder is modified. Includes the modified MAPIFolder as an argument. |
FolderRemove
|
Occurs after a folder has been deleted. |
Items Events
|
|
ItemAdd
|
Occurs when a new item is created. Includes the new item as an argument. |
ItemChange
|
Occurs when an item is modified. Includes the modified item as an argument. |
ItemRemove
|
Occurs after an item has been deleted. |
The FolderRemove
and ItemRemove
events have a severe limitation in that they fire only after the folder or item has been deleted—in other words, when it's too late to do anything about it! One workaround is to set up event handlers on the Deleted Items folder itself to watch for the addition of new folders and items. You will do a folder deletion monitor in the next section. Putting an event handler on the Deleted Items folder's Items
collection to watch for deleted items does not help you recover data if the user presses Shift+Delete to delete the item without going through Deleted Items. Outlook 2002 introduces a BeforeDelete
event for each type of Outlook item. You may want to incorporate it into your code for custom forms, as discussed in Chapter 18, "Writing Code to Respond to Outlook Form Events."
If you work in Outlook with the Folder List turned on, sooner or later you're bound to delete a folder accidentally. Outlook is good about asking you whether you really want to delete a folder, but it doesn't hurt to have extra protection. One application of Folders
events is to monitor the Deleted Items folder for any new folders added to it and ask users whether they really want to delete that folder.
As with other events, you must declare a Folders
or Items
object variable WithEvents
, initialize it, and write code for the event handler. As with the Explorer and Explorers events, we will create a new class module for folder events. Follow these steps:
- Choose
Insert
,Class Module
to create a new class module in VBA. - In the Properties window, change the value of the
(Name)
property toFolderEvents
. - Add the code in Listing 11.8to the
FolderEvents
module. - Edit the
ThisOutlookSession
module to include the code in Listing 11.9. If you already have anApplication_Startup
procedure, do not create a new one. Simply add the code from the procedure in Listing 11.9 to your existing routine. - Either exit and restart Outlook, or run the
Application_Startup
procedure to initialize the new event handlers.
Listing 11.7 Watching for deleted folders
Private WithEvents m_colDeletedItemsFolders _
As Outlook.FoldersPrivate Sub Class_Terminate()
Call DeRefFolders
End Sub
Public Sub InitFolders(objApp As Outlook.Application)
Dim objNS As Outlook.NameSpace
Set objNS = objApp.GetNamespace("MAPI")
Set m_colDeletedItemsFolders = _
objNS.GetDefaultFolder(olFolderDeletedItems).Folders
Set objNS = Nothing
End Sub
Public Sub DeRefFolders()
Set m_colDeletedItemsFolders = Nothing
End Sub
Private Sub m_colDeletedItemsFolders_FolderAdd _
(ByVal Folder As Outlook.MAPIFolder)
Dim objNS As Outlook.NameSpace
Dim objDestFolder As Outlook.MAPIFolder
Dim strMsg As String
Dim intRes As Integer
If Folder.Items.Count <> 0 Then
strMsg = "Did you really mean to delete the " & _
Folder.Name & " Folder?" & vbCrLf & _
vbCrLf & "If you click No, you " & _
"will need to choose the parent " & _
"folder where it belongs."
intRes = MsgBox(strMsg, _
vbYesNo + vbDefaultButton2 + vbQuestion, _
"Delete Folder?")
If intRes = vbNo Then
Set objNS = _
Folder.Application.GetNamespace("MAPI")
Set objDestFolder = objNS.PickFolder
If Not objDestFolder Is Nothing Then
Folder.MoveTo objDestFolder
End If
End If
End If
Set objNS = Nothing
Set objDestFolder = Nothing
End Sub
Listing 11.8 ThisOutlookSession code to handle Folders events
Dim m_folderevents As New FolderEventsPrivate Sub Application_Startup()
m_folderevents.InitFolders Application
End Sub
It's too bad that Outlook can't remember the original location of the folder before it was deleted. That's why Listing 11.8 must include a PickFolder
method (which you first saw in Listing 11.3), so the user can indicate where the folder should be relocated.
In Chapters 3 and 9, we worked on a birthday/anniversary reminder tool, a VBA form that updated all existing birthdays and/or anniversaries in your Calendar folder. But wouldn't it be nice if Outlook would automatically add a reminder without the need to run the VBA form periodically? This is a perfect job for event handlers that monitor the Calendar folder for new and modified items.
Add the code in Listing 11.10 to the FolderEvents
class module created in the previous section. If you created the InitFolders
and DeRefFolders
procedures in Listing 11.8, you don't need to add new ones. Just update the existing procedures to include the code from the corresponding subroutines in Listing 11.10. You can put all the initialization code for the various objects declared WithEvents
in this module into the InitFolders
and DeRefFolders
procedures.
Also put the code in Listing 11.9 in the previous section in the ThisOutlookSession
module, if you haven't done so already. Restart Outlook or run the Application_Startup procedure, then add a new birthday to a contact, and check the corresponding entry in the Calendar folder.
Listing 11.10 Adding reminders to birthday and appointments events
Private WithEvents m_colCalendarItems As Outlook.ItemsPrivate Sub Class_Terminate()
Call DeRefFolders
End Sub
Public Sub InitFolders(objApp As Outlook.Application)
Dim objNS As Outlook.NameSpace
Set objNS = objApp.GetNamespace("MAPI")
Set m_colCalendarItems = _
objNS.GetDefaultFolder(olFolderCalendar).Items
Set objNS = Nothing
End Sub
Public Sub DeRefFolders()
Set m_colCalendarItems = Nothing
End Sub
Private Sub m_colCalendarItems_ItemAdd _
(ByVal Item As Object)
Call UpdateReminder(Item)
End Sub
Private Sub m_colCalendarItems_ItemChange(ByVal Item As Object)
Call UpdateReminder(Item)
End Sub
Sub UpdateReminder(Item As Object)
Dim intDays As Integer
Dim strSubject As String
Dim blnDoUpdate As Boolean
' *** USER OPTIONS ***
' set number of days before event that the
' reminder should fire
intDays = 5
If Item.Class = olAppointment Then
strSubject = Item.Subject
If (InStr(strSubject, "Birthday") > 0 Or _
InStr(strSubject, "Anniversary") > 0) Then
blnDoUpdate = False
If Item.ReminderSet = False Then
blnDoUpdate = True
ElseIf Item.ReminderMinutesBeforeStart _
< 24 * 60 Then
blnDoUpdate = True
End If
If blnDoUpdate Then
With Item
.ReminderSet = True
.ReminderMinutesBeforeStart = _
intDays * 24 * 60
.Save
End With
End If
End If
End If
End Sub
A few things worth noticing in the code:
- Even though it's rare for a non-appointment item to get added to the Calendar folder, it's still a good idea to always check the
Class
property of an item of unknown type, as we do with theIf Item.Class = olAppointment Then
statement before using any properties specific to a certain type of item. - The
*** USER OPTIONS ***
section calls attention to the key setting for your reminder updates—the number of days ahead of the event. - The statement
ElseIf Item.ReminderMinutesBeforeStart < 24 * 60 Then
checks whether a reminder has been set for less than a day before the event, since Outlook will sometimes add its own reminder automatically.
Just as you might use the ItemAdd
event to monitor the Calendar folder for new birthdays or anniversaries, you can also monitor the Inbox for new incoming mail messages and thus build your own alternative to the Outlook Rules Wizard. There are a few caveats: If too many messages are received at once, Outlook will not fire the ItemAdd
event for each one. Also, it might not be a good idea to mix Rules Wizard rules and VBA processing of the Inbox, since it's not possible to predict which will process a message first. You might even see the extreme case: A rule fires and performs part of its actions, then ItemAdd
fires, then the rule finishes.
For a demonstration of the ItemAdd
technique, you need to create a new subfolder under your Inbox named Quarantine. (If you don't create the folder, the code in Listing 11.11 will create it for you.) Then add the code in Listing 11.11 to the FolderEvents
module, adding to the InitFolders
and DeRefFolders
subroutines, if you already created those following the steps in the previous two sections, rather than adding new ones. The DoQuarIFrame
subroutine moves any HTML mail message that contains an <iframe> tag into the Quarantine folder; such messages often carry viruses.
Listing 11.11 Moving suspicious mail messages to a Quarantine folder
Private WithEvents m_colInboxItems As Outlook.ItemsPrivate Sub Class_Terminate()
Call DeRefFolders
End Sub
Public Sub InitFolders(objApp As Outlook.Application)
Dim objNS As Outlook.NameSpace
Set objNS = objApp.GetNamespace("MAPI")
Set m_colInboxItems = _
objNS.GetDefaultFolder(olFolderInbox).Items
Set objNS = Nothing
End Sub
Public Sub DeRefFolders()
Set m_colInboxItems = Nothing
End Sub
Private Sub m_colInboxItems_ItemAdd(ByVal Item As Object)
Dim blnItemMoved As Boolean
blnItemMoved = QuarIFrame(Item)
End Sub
Function QuarIFrame(objItem As Outlook.MailItem) As Boolean
Dim objNS As Outlook.NameSpace
Dim objInbox As Outlook.MAPIFolder
Dim objQuarFolder As Outlook.MAPIFolder
On Error Resume Next
Set objNS = objItem.Application.GetNamespace("MAPI")
Set objInbox = objNS.GetDefaultFolder(olFolderInbox)
Set objQuarFolder = objInbox.Folders.Item("Quarantine")
If objQuarFolder Is Nothing Then
Set objQuarFolder = _
objInbox.Folders.Add("Quarantine")
End If
If Not objQuarFolder Is Nothing Then
If objItem.Class = olMail Then
If InStr(1, objItem.HTMLBody, _
"<IFRAME", vbTextCompare) > 0 Then
objItem.Move objQuarFolder
QuarIFrame = True
End If
End If
End If
Set objNS = Nothing
Set objInbox = Nothing
Set objQuarFolder = Nothing
End Function
This time, instead of calling a subroutine, as the event handlers in Listing 11.10 called the UpdateReminder
procedure, the m_colInboxItems_ItemAdd
event handler gets the value of a function QuarIFrame()
, which returns True
whenever it moves a suspicious messages containing an <iframe> tag to the Quarantine folder. This modular approach sets up a framework for putting several VBA-based "rules" in the m_colInboxItems_ItemAdd
event handler. If an item has already been handled and moved from the Inbox, you probably don't want to handle it again, so you can check the value of blnItemMoved
before proceding to a second function:
blnItemMoved = QuarIFrame(Item)
If blnItemMoved Then Exit Sub
blnItemMoved = SecondRule(Item)
**Note **If
blnItemMoved
is
True
, code execution exits the subroutine and does no further processing on the message. But if
blnItemMoved
is
False
, the
SecondRule()
function is processed. Building an
ItemAdd
event handler for the Items collection of the Inbox folder is not your only option for automatically processing incoming items. You could also use the
Application.NewMail
event. However, since the
NewMail
event does not provide any details about the arriving item(s), you would have to keep track of what items were previously processed and, if you are also running Rules Wizard rules, examine multiple folders for new items.
In Outlook 2002, you have the option of running a VBA subroutine from a rule, as described in Chapter 4, "Code Basics."
Outlook 2002 adds a new Reminders
collection of Reminder
objects with associated events that fire when reminders are created, modified, or removed; when reminders fire; and when the user snoozes or dismisses a reminder. Table 11.5 lists the Reminders events.
Table 11.5 Reminders
events
Event | Description |
---|---|
BeforeReminderShow |
Occurs before Outlook displays a reminder (before ReminderFire ). Cancelable. |
ReminderAdd |
Occurs after a new reminder has been created; includes the item that fired the reminder as an argument |
ReminderChange |
Occurs after a reminder has been changed; includes the item that fired the reminder as an argument |
ReminderFire |
Occurs right before a reminder fires; includes the item that fired the reminder as an argument |
ReminderRemove |
Occurs when a user dismisses a reminder, deletes an item that contains a reminder, or turns off the reminder for an item; also occurs when a reminder is dismissed programmatically with the Reminder.Dismiss method or removed from the Reminders collection |
Snooze |
Occurs when the user clicks the Snooze button on the Reminders dialog or when a reminder is snoozed programmatically with the Reminder.Snooze method; includes the item that fired the reminder as an argument |
For those events with a ReminderObject
argument, this argument represents a Reminder object. Keep in mind that only items in the default Inbox, Calendar, Contacts, and Tasks folders can have reminders associated with them.
To work with Reminders events, you must declare a Reminders
object WithEvents
and instantiate it, just as you did with the Explorers, Folders, and Items collections. Follow these steps to create a RemindersEvents
class module and update ThisOutlookSession
with the necessary code:
- Choose Insert, Class Module to create a new class module in VBA.
- In the Properties window, change the value of the
(Name)
property toRemindersEvents
. - Add the code in Listing 11.12 to the
RemindersEvents
module. - Edit the
ThisOutlookSession
module to include the code in Listing 11.13. If you already have anApplication_Startup
procedures, do not create a new one. Just add the code from the procedure in Listing 11.13 to your existing routine. - Either exit and restart Outlook, or run the
Application_Startup
procedure to initialize the new event handlers.
Listing 11.12 Reminders event handlers and initialization code
Dim WithEvents m_colReminders As Outlook.Reminders
Dim m_intBusyStatus As IntegerPrivate Sub Class_Terminate()
Call DeRefReminders
End Sub
Public Sub InitReminders(objApp As Outlook.Application)
Set m_colReminders = objApp.Reminders
m_intBusyStatus = 0
End Sub
Public Sub DeRefReminders()
Set m_colReminders = Nothing
End Sub
Private Sub m_colReminders_ReminderFire _
(ByVal ReminderObject As Outlook.Reminder)
Dim strMsg As String
Const ME_BUSY = vbYes
Const ME_NOT_BUSY = vbNo
Const ME_UNKNOWN_BUSY = 0
If m_intBusyStatus = ME_UNKNOWN_BUSY Then
strMsg = "Are you really busy today?"
m_intBusyStatus = MsgBox(strMsg, _
vbYesNo + vbDefaultButton2 + vbQuestion, _
"Busy Day?")
End If
If m_intBusyStatus = ME_BUSY Then
If ReminderObject.IsVisible Then
ReminderObject.Snooze (24 * 60)
End If
End If
End Sub
Private Sub m_colReminders_Snooze _
(ByVal ReminderObject As Outlook.Reminder)
Dim objItem As Object
Dim dteNextReminder As Date
Dim strItemType As String
Dim strMsg As String
Dim intRes As Integer
Set objItem = ReminderObject.Item
dteNextReminder = ReminderObject.NextReminderDate
If objItem.Importance = olImportanceHigh Then
If DateDiff("h", Date, dteNextReminder) >= 24 Then
strMsg = Replace(TypeName(objItem), "Item", "")
strMsg = "You just snoozed the reminder " & _
" for " & vbCrLf & vbCrLf & vbTab & _
strMsg & ": " & objItem.Subject & _
vbCrLf & vbCrLf & " until " & _
FormatDateTime(dteNextReminder) & _
"." & vbCrLf & vbCrLf & _
"Did you really want to do that? " & _
"Click No to edit the item and " & _
"change the reminder."
intRes = MsgBox(strMsg, _
vbYesNo + vbDefaultButton2 + vbQuestion, _
"You Snoozed an Important Reminder !")
If intRes = vbNo Then
objItem.Display
End If
End If
End If
Set objItem = Nothing
End Sub
Listing 11.13 ThisOutlookSession
code to handle Reminders
events
Dim m_remindevents As New RemindersEventsPrivate Sub Application_Startup()
m_remindevents.InitReminders Application
End Sub
Notice that using Reminder.Snooze
in a ReminderFire
event handler means that the reminder will not be displayed to the user.
A few more sets of events deserve attention. Outlook supports several events associated with synchronization of Outlook with an Exchange Server mailbox using a SyncObject
object. The SyncObject
object is a named set of synchronization settings or, in Outlook 2002, send/receive group settings. Table 11.6 lists its events.
Table 11.6 SyncObject
Events
Event | Description |
---|---|
OnError |
Occurs when an error occurs during synchronization. Includes the error Code and Description as arguments. |
Progress |
Occurs periodically during a synchronization session. Includes several arguments providing information on the process, including the number of items to be synchronized. |
SyncEnd |
Occurs after synchronization has completed. |
SyncStart |
Occurs when synchronization begins. |
An event handler for a SyncObject
event might be initialized in a procedure that uses the Start
method to perform synchronization using a particular SyncObject
object. You could also choose to initialize it in the Application_StartUp
procedure by referencing a particular item in the Application.Session.SyncObjects
collection.
Outlook 2002 adds a Views
collection of all folder views plus ViewAdd
and ViewRemove
objects that fire, respectively, when a new view is added or an existing view is removed.
Finally, in Chapter 21, "Menus, Toolbars, and the Outlook Bar," you will examine events related to the Outlook Bar.
VBA event handling takes Outlook programming to a new level of utility not available in versions before Outlook 2000. Through events related to the application itself—and Outlook's folders, windows, reminders, and other components—you gain much greater control over what happens in Outlook.
Beyond the events, you also learned two useful methods for popping up dialogs where users can select a folder or choose categories—PickFolder
and ShowCategoriesDialog
(which is available only in Outlook 2002). Furthermore, you saw an example of working with the CDO Fields
collection to access a property that the Outlook object model does not expose.
In the Chapter 12, you will learn more techniques for working with folders and the Explorer
windows that display folders.