I am trying to implement Adrian Brown's very nice Outlook Add-In code and it works 2 out of 3 times. ItemAdd
and ItemChange
events are firing as expected, but the event handler for MAPIFolderEvents_12_Event.BeforeItemMove
does not appear to be doing anything - I don't even hit a breakpoint on the first line of the event handler.
More Code for Clarity
This is the CalendarMonitor class; it monitors ItemAdd, ItemChange events on the Items collection of the folder, as well as BeforeItemMove on the MAPIFolder:
public class CalendarMonitor
{
private Explorer _explorer;
private List<string> _folderPaths;
private List<MAPIFolder> _calendarFolders;
private List<Items> _calendarItems;
private MAPIFolder _deletedItemsFolder;
public event EventHandler<EventArgs<AppointmentItem>> AppointmentAdded;
public event EventHandler<EventArgs<AppointmentItem>> AppointmentModified;
public event EventHandler<CancelEventArgs<AppointmentItem>> AppointmentDeleting;
public CalendarMonitor(Explorer explorer)
{
_folderPaths = new List<string>();
_calendarFolders = new List<MAPIFolder>();
_calendarItems = new List<Items>();
_explorer = explorer;
_explorer.BeforeFolderSwitch += Explorer_BeforeFolderSwitch;
var session = _explorer.Session;
try
{
_deletedItemsFolder = session.GetDefaultFolder(OlDefaultFolders.olFolderDeletedItems);
HookupDefaultCalendarEvents(session);
}
finally
{
Marshal.ReleaseComObject(session);
session = null;
}
}
private void HookupDefaultCalendarEvents(_NameSpace session)
{
var folder = session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);
if (folder == null) return;
try
{
HookupCalendarEvents(folder);
}
finally
{
Marshal.ReleaseComObject(folder);
folder = null;
}
}
private void Explorer_BeforeFolderSwitch(object obj, ref bool cancel)
{
var folder = (obj as MAPIFolder);
if (folder == null) return;
try
{
// Hookup events to any other Calendar folder opened.
if (folder.DefaultItemType == OlItemType.olAppointmentItem)
HookupCalendarEvents(folder);
}
finally
{
Marshal.ReleaseComObject(folder);
folder = null;
}
}
private void HookupCalendarEvents(MAPIFolder calendarFolder)
{
if (calendarFolder.DefaultItemType != OlItemType.olAppointmentItem)
{
throw new ArgumentException("The MAPIFolder must use AppointmentItems as the default type.");
}
// Ignore other user's calendars.
if (_folderPaths.Contains(calendarFolder.FolderPath) || (!IsUsersCalendar(calendarFolder))) return;
var items = calendarFolder.Items;
// Store folder path to prevent repeating listeners
_folderPaths.Add(calendarFolder.FolderPath);
// Store a reference to the folder & items to prevent garbage collection
_calendarFolders.Add(calendarFolder);
_calendarItems.Add(items);
// Add listeners
((MAPIFolderEvents_12_Event)calendarFolder).BeforeItemMove += Calendar_BeforeItemMove;
items.ItemChange += CalendarItems_ItemChange;
items.ItemAdd += CalendarItems_ItemAdd;
}
private void CalendarItems_ItemAdd(object obj)
{
var appointment = (obj as AppointmentItem);
if (appointment == null) return;
try
{
if (AppointmentAdded != null)
AppointmentAdded(this, new EventArgs<AppointmentItem>(appointment));
}
finally
{
Marshal.ReleaseComObject(appointment);
appointment = null;
}
}
private void CalendarItems_ItemChange(object obj)
{
var appointment = (obj as AppointmentItem);
if (appointment == null) return;
try
{
if (AppointmentModified != null)
AppointmentModified(this, new EventArgs<AppointmentItem>(appointment));
}
finally
{
Marshal.ReleaseComObject(appointment);
appointment = null;
}
}
private void Calendar_BeforeItemMove(object obj, MAPIFolder moveToFolder, ref bool cancel)
{
if ((moveToFolder != null) && (!IsDeletedItemsFolder(moveToFolder))) return;
var appointment = (obj as AppointmentItem);
if (appointment == null) return;
try
{
if (AppointmentDeleting == null) return;
// Listeners to the AppointmentDeleting event can cancel the move operation if moving
// to the deleted items folder.
var args = new CancelEventArgs<AppointmentItem>(appointment);
AppointmentDeleting(this, args);
cancel = args.Cancel;
}
finally
{
Marshal.ReleaseComObject(appointment);
appointment = null;
}
}
private bool IsUsersCalendar(MAPIFolder folder)
{
// This is based purely on my observations so far - a better way?
return (folder.Store != null);
}
private bool IsDeletedItemsFolder(MAPIFolder folder)
{
return (folder.EntryID == _deletedItemsFolder.EntryID);
}
public AppointmentItem Item { get; set; }
}
New Information:
I have done some additional "troubleshooting" and come up with more information: on a whim, I created a new calendar in Outlook (while debugging) and lo and behold the BeforeItemMove
event fires just like I expect it to when deleting an appointment in the new calendar, but it still doesn't work in the original.
If I exit the debug session and restart, neither calendar's event functions as expected, despite working fine earlier. Any new calendar's BeforeItemMove
event will work fine, until I close Outlook - then it's back to not responding.
I am hoping that this additional information will provide insight to those wiser than I. Any assistance is greatly appreciated.