Introduction
I have recognized there is no enough information about OutlookAddIn programming in the internet therefore I decided to write this article. I have searched many website and I have read some books interesting AddIn Programming. After some of my professional works I have find out some critical solutions for new AddIn Programmers and I am here.
This article identifying some critical details interesting with:
Contents
- Regions
- EventHandlers
- Reports
- Task, Mail, and Appointment Management
- Operational System and Version Detection
- Views
- Rules
Startup
An add-in starts with some event handler because if programmer wants to do some things according some conditions in an office program it gives up an event to the programmer and he can handle this event and he can use according his aims. An event handler begins like below at the startup:
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
After this progress Programmer may use next steps:
public static Outlook.Inspectors inspectors;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors.NewInspector +=
new Microsoft.Office.Interop.Outlook.
InspectorsEvents_NewInspectorEventHandler(inspectors_NewInspector);
_instance = this;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
Logic
Inserting a Button to the Tool Bar or Context Menu, catching some screens for changing their algorithms according the necessities and releasing full requirements is the main factors in an AddIn program. So, we can write all this phases starting from the inspector which we have handled in the Start Up event before. Current Item gives us the substantial element when the event fired. I have handled Appointment Item in this case like below. I will use timer object to draw a graphic to the current window that user has entered. After this operation I am calling the ReportInspector function for an inspection if any report is exist than I am sending this report to the current user as a mail.
void inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
_manuelCancel = false;
Object anitem = (Outlook.AppointmentItem)Inspector.CurrentItem;
if (anitem != null)
{
_aitm = (Outlook.AppointmentItem)anitem;
if (_aitm.Body == null)
{
_tm.Interval = 10;
_tm.Tick += new EventHandler(tm_Tick);
_timerSwitch = true;
_tm.Start();
}
}
_isLiiveMessageShowing = IsLiveMeetingShowing(DateTime.Now, Inspector);
ReportInspector(DateTime.Now, Inspector);
}
When we have handled the new appointment window than we can capture its content over to make a new special design by using some tables and graphical COM Objects. The content is a Document Class in here.
void tm_Tick(object sender, EventArgs e)
{
if (_timerSwitch)
{
try
{
_timerSwitch = false;
_tm.Stop();
if (((inspectors[1].WordEditor as
Microsoft.Office.Interop.Word.DocumentClass)
).InlineShapes.Count < 1)
{
object ranger = (inspectors[1].WordEditor as
Microsoft.Office.Interop.Word.DocumentClass
).Application.Selection.Range;
if (((inspectors[1].WordEditor as
Microsoft.Office.Interop.Word.DocumentClass)).Tables.Count == 0)
((inspectors[1].WordEditor as
Microsoft.Office.Interop.Word.DocumentClass)).Tables.Add(
range, 2, 2, ref defaultBehavior, ref autoFitBehavior);
Microsoft.Office.Interop.Word.Cell vertical1 =
((inspectors[1].WordEditor as
Microsoft.Office.Interop.Word.DocumentClass)
).Tables[1].Columns[1].Cells[1];
Microsoft.Office.Interop.Word.Column clm1 =
((inspectors[1].WordEditor as
Microsoft.Office.Interop.Word.DocumentClass)).Tables[1].Columns[1];
clm1.Width = 40;
area1.Range.Font.Name = "Calibri";
merger3.Merge(vertical1);
vertical1.Range.Font.Size = 22;
vertical1.Range.Font.Name = "Calibri";
vertical1.Height = 200;
vertical1.Range.Bold = 12;
vertical1.Range.Text = "";
vertical1.Range.Orientation =
Microsoft.Office.Interop.Word.WdTextOrientation.wdTextOrientationUpward;
vertical1.VerticalAlignment =
Microsoft.Office.Interop.Word.WdCellVerticalAlignment.wdCellAlignVerticalCenter;
vertical1.Range.Shading.BackgroundPatternColor =
Microsoft.Office.Interop.Word.WdColor.wdColorBrightGreen;
vertical1.Range.FitTextWidth = 0;
vertical1.Range.Font.Color = Microsoft.Office.Interop.Word.WdColor.wdColorWhite;
area1.Range.Borders.OutsideLineStyle =
Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleThinThickLargeGap;
cellHorizontal1.Range.Borders.OutsideLineStyle =
Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleThinThickLargeGap;
cellHorizontal1.Range.Borders[
Microsoft.Office.Interop.Word.WdBorderType.wdBorderTop].Color =
Microsoft.Office.Interop.Word.WdColor.wdColorGray05;
vertical1.Borders[Microsoft.Office.Interop.Word.WdBorderType.wdBorderBottom].Color =
Microsoft.Office.Interop.Word.WdColor.wdColorGray05;
Outlook.AppointmentItem app = inspectors[1].CurrentItem as Outlook.AppointmentItem;
((Outlook.ItemEvents_10_Event)app).Close +=
new ItemEvents_10_CloseEventHandler(ThisAddIn_Close);
((Outlook.ItemEvents_10_Event)app).Send +=
new ItemEvents_10_SendEventHandler(ThisAddIn_Send);
}
}
catch (System.Exception ex)
{
Outlook.AppointmentItem outlookAppointmentItem =
(Outlook.AppointmentItem)inspectors[2].CurrentItem;
outlookAppointmentItem.Close(OlInspectorClose.olDiscard);
outlookAppointmentItem.Delete();
}
}
}
Close and Send Event handlers are providing some kind of special behaviors when this item has been sending or closing by the user interaction. I have used many control code when this item has been closed and sending. I have recognized which button user has entered from the algorithms in the Close event because Close event is working in every scenario which user has altered in the user interface.
public void ThisAddIn_Send(ref bool Cancel)
{
if (!Cancel && IsLiveMeeting((inspectors[1].CurrentItem as
Outlook.AppointmentItem).RequiredAttendees,
(inspectors[1].CurrentItem as Outlook.AppointmentItem).Location))
{
WarningLiveMeeting wlm = new WarningLiveMeeting();
wlm.StartPosition = FormStartPosition.CenterScreen;
if (wlm.ShowDialog() == DialogResult.OK)
{
if (wlm._sellection)
{
Cancel = true;
return;
}
}
}
(((inspectors[1].WordEditor as Microsoft.Office.Interop.Word.DocumentClass)
).Content as Outlook.MailItem).BodyFormat = OlBodyFormat.olFormatUnspecified;
}
Reports and Functionality
We can get periodical statistics as a report in some way. Also we can take some reports from Exchange Server. I will show you some personal reports taking by this scenario. The GetMonthlyReport
function is getting the default MAPI folder to identifying how many meeting user has released how much time he spent in last month.
public List<Outlook.AppointmentItem> GetMonthlyReport()
{
counter = 0;
Outlook.MAPIFolder mpiFolder =
Application.GetNamespace("MAPI").GetDefaultFolder(
OlDefaultFolders.olFolderCalendar);
var appItems = new List<Outlook.AppointmentItem>();
foreach (object obj in mpiFolder.Items)
{
counter++;
AppointmentItem appointment = obj as AppointmentItem;
if (appointment != null)
{
if (appointment.Start.Month == DateTime.Now.Month -1)
{
if (appointment.RequiredAttendees == null ||
appointment.RequiredAttendees.Equals(string.Empty))
{
_meetingCountOwner++;
}
if (appointment.MeetingStatus == OlMeetingStatus.olMeeting ||
appointment.MeetingStatus == OlMeetingStatus.olMeetingReceived ||
appointment.RequiredAttendees == null ||
appointment.RequiredAttendees.Equals(string.Empty))
{
_meetingCount++;
_totalMeetingTime += appointment.End - appointment.Start;
}
if (appointment.Subject.Contains("Live Meeting") ||
appointment.Subject.Contains("LiveMeeting") ||
appointment.Subject.Contains("livemeeting") ||
appointment.Subject.Contains("live meeting"))
_livmeetingCount++;
}
}
}
return appItems;
}
This case is also looking for his live meetings and accepted meetings. Also we can use this routine in other folders too checking their periodical background. For example, getting featured Appointments, Tasks and Mails like below.
public List<Outlook.AppointmentItem> GetFeaturedAppointments()
{
Outlook.MAPIFolder mpiFolder =
Application.GetNamespace("MAPI").GetDefaultFolder(OlDefaultFolders.olFolderCalendar);
var appItems = new List<Outlook.AppointmentItem>();
foreach (object obj in mpiFolder.Items)
{
AppointmentItem appointment = obj as AppointmentItem;
if (appointment != null)
{
if (appointment.Start > DateTime.Now - new TimeSpan(0, 30, 0))
appItems.Add(appointment);
}
}
return appItems;
}
public List<Outlook.TaskItem> GetFeaturedTasks()
{
Outlook.MAPIFolder mpiFolder =
Application.GetNamespace("MAPI").GetDefaultFolder(
OlDefaultFolders.olFolderTasks);
var taskItems = new List<Outlook.TaskItem>();
foreach (object obj in mpiFolder.Items)
{
TaskItem task = obj as TaskItem;
if (task != null)
{
taskItems.Add(task);
}
}
return taskItems;
}
public List<Outlook.MailItem> GetFeaturedMails()
{
Outlook.MAPIFolder mpiFolder =
Application.GetNamespace("MAPI").GetDefaultFolder(
OlDefaultFolders.olFolderSentMail);
var mailItems = new List<Outlook.MailItem>();
foreach (object obj in mpiFolder.Items)
{
MailItem mail = obj as MailItem;
if (mail != null)
{
mailItems.Add(mail);
}
}
return mailItems;
}
Version Detection
The Detection of the Op. System is out from this article’s target but I want to show you how you can detect this AddIn’s version and how we can write an AddIn according other Op. Systems and according other versions of the office. I will show Op. System detection routine before as below.
private string FindVersion()
{
System.OperatingSystem osInfo = System.Environment.OSVersion;
string operatingSystem = "Unknown";
switch (osInfo.Platform)
{
case System.PlatformID.Win32Windows:
switch (osInfo.Version.Minor)
{
case 0:
operatingSystem = "Windows 95";
break;
case 10:
operatingSystem = "Windows 98";
break;
case 90:
operatingSystem = "Windows Me";
break;
}
break;
case System.PlatformID.Win32NT:
switch (osInfo.Version.Major)
{
case 3:
operatingSystem = "Windows NT 3.51";
break;
case 4:
operatingSystem = "Windows NT 4.0";
break;
case 5:
if (osInfo.Version.Minor == 0)
operatingSystem = "Windows 2000";
else
operatingSystem = "XP";
break;
case 6:
operatingSystem = "Win7";
break;
}
break;
}
return operatingSystem;
}
When we have got the system knowledge then we can write any opportunities according it’s properties. Anyway, the real important point here is the Version detection which I have prepared like below.
string version = ((Microsoft.Office.Interop.Outlook.ApplicationClass)(
((Microsoft.Office.Interop.Outlook.InspectorClass)(_inspector)).Application)).Version;
int preVer = Convert.ToInt32(version.Substring(0, 2));
While preVer indicates 12 than office version is 2007 and when preVer indicates 14 than office version is 2007.
I was teach from this work Microsoft office 2007 Sp1 has a hot fix for office 2007 object model’s Close and Save events supporting. Even office 2010 does not have this opportunity yet.
Views
Views are providing customized screen and action management in outlook. User can make settings from tool menu but I want to prepare this feature by programmatically in C#.
private void CreateCustomAksiyonViewIfNotExist()
{
Application.ActiveExplorer().CurrentFolder.CustomViewsOnly = true;
Outlook.View lastView = Application.ActiveExplorer().CurrentFolder.CurrentView;
Outlook.View newView = null;
try
{
bool isthere = false;
Application.ActiveExplorer().NavigationPane.CurrentModule =
Application.ActiveExplorer().NavigationPane.Modules[4];
foreach (Outlook.View view in Application.ActiveExplorer().CurrentFolder.Views)
{
if (view.Name.Equals("Actions"))
isthere = true;
}
if (!isthere)
{
newView = Application.ActiveExplorer().CurrentFolder.Views.Add("Actions",
Outlook.OlViewType.olTableView,
Outlook.OlViewSaveOption.olViewSaveOptionThisFolderEveryone);
newView.Filter = "\"urn:schemas:httpmail:subject\" LIKE '% Actions %'";
newView.Save();
newView.Apply();
}
}
catch (System.ArgumentException ex)
{
MessageBox.Show(ex.Message);
return;
}
Application.ActiveExplorer().NavigationPane.CurrentModule =
Application.ActiveExplorer().NavigationPane.Modules[1];
}
Rules
Rules look likes Views as a user customization but the Rules are relying on just actions and special contents when actions are releasing.
void CreateActionRule()
{
Rules ruless = this.Application.Session.DefaultStore.GetRules();
for (int k = 1; k < ruless.Count; k++)
{
if (ruless[k].Name.Equals("ActionAndDecissionRule"))
return;
}
Outlook.MAPIFolder inbox = this.Application.Session.GetDefaultFolder(
Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
inbox.Folders.Add("Aksiyon&Kararlar",
Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
Outlook.MAPIFolder targetfolder = inbox.Folders["Aksiyon&Kararlar"];
Rules rules = this.Application.Session.DefaultStore.GetRules();
Outlook.Rule rule = rules.Create("ActionAndDecissionRule",
Microsoft.Office.Interop.Outlook.OlRuleType.olRuleReceive);
Outlook.TextRuleCondition sub = rule.Conditions.Body;
sub.Enabled = true;
sub.Text = new string[] { "ToplantisiKararlari-iyitoplantiKontrol" };
Outlook.MoveOrCopyRuleAction movecopy = rule.Actions.MoveToFolder;
movecopy.Enabled = true;
movecopy.Folder = targetfolder;
rule.Execute(true, inbox, false,
Outlook.OlRuleExecuteOption.olRuleExecuteUnreadMessages);
rules.Save(false);
}
Conclusion
In conclusion, I have researched many documents and I have not found enough information on this subject and there is no way to get a free book in this field. I am Suring this information will be useful when someone wants to write an AddIn.
Annexes