我的VSTO之路(五):Outlook初步开发之联系人扩展
上一讲我们完成对Word的介绍,文本开始,我将着重介绍Outlook。Outlook是微软Office中一个非常实用的工具,尤其在一个拥有Windows Domain的公司局域网中,Outlook是员工最常用的通讯工具,所以对Outlook实行进一步的定制开发的需求量是很大的。本文中,我先以联系人的扩展为开始,讲解如何开发一个强大的Outlook插件。
故事的开始
首先我们假设一个场景。有一天,市场部的同事来找你帮个小忙(有经验的人都知道,一般这都是无尽痛苦的开始)。他们希望在Outlook的联系人信息上加入对方父母生日信息,方便他们做市场营销(嘿嘿,怎么营销大家自己想咯)。同时,最好能够和Outlook原来的联系人界面保持一致便于查阅。为了不辜负同事的期望,你打开了Visual Studio……
简单思索过后,你在脑海中浮现了一下两个功能点:
- Outlook Form Region:用于制作界面,可以和原本的Outlook界面整合在一起。
- Contact User Define Fields:用于保存新加入的属性。
而新的联系人界面,你打算画成这样:
业务逻辑:每次Contact界面展示的时候,我们制作的Form Region会读联系人中用户自定义属性里面的值(我们分别为父母的名字和生日创建4个用户属性),并展示在界面上。当Contact关闭的时候,如果用户改变了这四个控件的值,我们将其写回到自定义属性中去。关于用户自定义的属性,如下图:
Outlook Object Model介绍
正式开始之前,我们先了解一下Outlook的对象模型。
Microsoft.Office.Interop.Outlook.Application
Outlook中的Application对象就像我在之前在Word中介绍过的Application对象一样,是所有对象的最顶层。即便你同时开多个Outlook,你也只创建了一个Application(进程也只有一个)。Application对象中,我们需要重点关注三个地方:
- CreateItem方法:通过这个方法,我们创建新的Outlook对象,例如Email、Task、Contact等。
- Explorers属性: 获得当前的Outlook.Explorer对象集合。
- Inspectors属性: 获得当前的Outlook.Inspector对象集合。
有很多方法可以获得Application对象,比较常用的是直接在Addin工程中,通过Globals.ThisAddIn.Application获得。如果你看过我之前写的Word插件,你会发现这里是和Word VSTO,其实这是微软为我们设定的模式。包括在之后的Excel和PowerPoint中也都是这样的。我这里进一步介绍一下Globals,它是一个在ThisAddIn.Designer.cs中定义的一个类,对于Outlook来说,它定义了三个静态属性,ThisAddin、Ribbons和Inspectors对象。Globals作用是,通过它我们可以在Addin项目的任何位置获得我们所需要的上下文。
Microsoft.Office.Interop.Outlook.Explorer
Explorer类,即当前Outlook的主窗口。示显示包含项(如电子邮件、任务或约会)的文件夹内容的窗口。Explorer 类包括可用来修改窗口的方法和属性,以及窗口更改时所引发的事件。需要注意的是,Outlook是可以开多个Explorer,但是只有一个Application。这点和Word类似,而和我们以后会讲到的Excel不同。一般我们通过Globals.ThisAddIn.Application.ActiveExplorer()方法来获得当前(焦点所在)的Explorer。
Microsoft.Office.Interop.Outlook.Inspector
Inspector类,即一个Outlook Item的窗口,比如你写邮件时弹出的窗口就是一个Inspector,你创建一个新的联系人也是一个Inspector。Inspector类是在开发Outlook插件中很常用的。尤其是以下几个地方:
- 获得当前的Inspector:Globals.ThisAddIn.Application.ActiveInspector(),很多时候我们都会通过这个方法获得现在的Inspector,但是这个不一定靠的住,我在今天的例子中就会讲到一种特殊情况。
- 新Inspector创建事件:Globals.ThisAddIn.Application.Inspectors.NewInspector,如果你需要为每个Email加一个Task Pane,就需要使用到这个事件。
- 获得Inspector对应的Outlook对象:Inspector.CurrentItem,Inspector只是一个窗口,而这个窗口背后的Outlook对象,则需要通过这个方法取得。
Microsoft.Office.Interop.Outlook.MAPIFolder
MAPIFolder即Outlook中的目录,Outlook中提供了16种内建的目录类型,由枚举Microsoft.Office.Interop.Outlook.OlDefaultFolders定义。
Microsoft.Office.Interop.Outlook.MailItem
Microsoft.Office.Interop.Outlook.AppointmentItem
Microsoft.Office.Interop.Outlook.TaskItem
Microsoft.Office.Interop.Outlook.ContactItem
分别对应Outlook中的Email、MeetingRequest、Task和Contact,它们都可以通过Application对象的CreateItem方法来创建。
Outlook Form Region
介绍
为了更好地制作界面,我们使用的是Outlook Form Region。它是从Outlook 2007开始,微软进入一项新的技术。它提供一种更加方便的方式扩展Outlook项目的界面。相对之前的Custom Form,Form Region是基于.net,开发更加容易,而且和Outlook本身,结合得更加紧密。
创建Form Region
首先,我们添加一个新的Item,选择Office项目类型中的Outlook Form Region
我们选择设计一个新的Form Region
Form Region的类型为Adjoining,即增加在页面的底部。
如果你需要在Region Form上添加比较多的控件,你也可以选择Separate,这样你就会有一个完整的空Form。它成为一个独立的标签页,不合原始的Form冲突。
为Form Region 起名字,并且设定在编辑模式和阅读模式中都显示我们的Region Form。
我们的Form Region是嵌入在Contact中的。
完成了这一系列的配置之后,VS会帮我们创建一个空的Form Region,如果需要修改刚才的设置,可以它在属性中修改,如下图。
我们开始往这个Form上添加控件,一共两个textbox、两个datetimepicker:
业务逻辑实现
流程图
这是根据我们的业务逻辑代码制作的流程图。
源代码分析
成员变量
1: // 自定义的属性名字
2: private const string PROPERTY_NAME_MOTHER_NAME = "PROPERTY NAME MOTHER NAME";
3: private const string PROPERTY_NAME_MOTHER_BIRTHDAY = "PROPERTY NAME MOTHER BIRTHDAY";
4: private const string PROPERTY_NAME_FATHER_NAME = "PROPERTY NAME FATHER NAME";
5: private const string PROPERTY_NAME_FATHER_BIRTHDAY = "PROPERTY NAME FATHER BIRTHDAY";
6:
7: // 自定义属性对象
8: private Outlook.UserProperty _MotherNameProperty = null;
9: private Outlook.UserProperty _MotherBirthdayProperty = null;
10: private Outlook.UserProperty _FatherNameProperty = null;
11: private Outlook.UserProperty _FatherBirthdayProperty = null;
12:
13: // 对应的Contact对象
14: public Outlook.ContactItem _Contact = null;
15:
16: // 标记是否内容修改
17: private bool _Changed = false;
FormRegionShowing事件,这里我们从Contact的用户自定义属性中取得值,并赋值给控件。如果这些属性不存在,我们则创建它们。同时我们所有的控件都绑定了Changed事件,便于我们判断值是否改变。这里有一个需要注意的地方,这里我需要获得Contact对象才能读取Property,但是如果你的Form Region是Adjoining模式的,在FormRegionShowing事件中,你会发现你通过Globals.ThisAddIn.Application.ActiveInspector()是不能取到Inspector的,进一步你也不能通过Inspector获得CurrentItem。这个问题在微软的论坛上都很少被提及。我这里所采用的方式是((EmployeeFamilyForm)sender).OutlookItem。可以看如下的代码:
1: private void EmployeeFamilyForm_FormRegionShowing(object sender, System.EventArgs e)
2: {
3: // 获得FormRegion所对应的Contact对象
4: _Contact = ((EmployeeFamilyForm)sender).OutlookItem as Outlook.ContactItem;
5:
6: // 从联系人的自定义属性中,获得母亲姓名属性
7: _MotherNameProperty = _Contact.UserProperties.Find(PROPERTY_NAME_MOTHER_NAME, Type.Missing);
8: if (_MotherNameProperty != null)
9: {
10: // 如果存在这个属性,则取出Value为控件赋值
11: tbMotherName.Text = _MotherNameProperty.Value as String;
12: }
13: else
14: {
15: // 不存在则创建这个属性
16: _MotherNameProperty = _Contact.UserProperties.Add(PROPERTY_NAME_MOTHER_NAME, Outlook.OlUserPropertyType.olText, Type.Missing, Type.Missing);
17: }
18:
19: // 母亲生日,原理相同
20: _MotherBirthdayProperty = _Contact.UserProperties.Find(PROPERTY_NAME_MOTHER_BIRTHDAY, Type.Missing);
21: if (_MotherBirthdayProperty != null)
22: {
23: dtpMotherBirthday.Value = (DateTime)_MotherBirthdayProperty.Value;
24: }
25: else
26: {
27: _MotherBirthdayProperty = _Contact.UserProperties.Add(PROPERTY_NAME_MOTHER_BIRTHDAY, Outlook.OlUserPropertyType.olDateTime, Type.Missing, Type.Missing);
28: }
29:
30: // 父亲姓名
31: _FatherNameProperty = _Contact.UserProperties.Find(PROPERTY_NAME_FATHER_NAME, Type.Missing);
32: if (_FatherNameProperty != null)
33: {
34: tbFatherName.Text = _FatherNameProperty.Value as String;
35: }
36: else
37: {
38: _FatherNameProperty = _Contact.UserProperties.Add(PROPERTY_NAME_FATHER_NAME, Outlook.OlUserPropertyType.olText, Type.Missing, Type.Missing);
39: }
40:
41: // 父亲生日
42: _FatherBirthdayProperty = _Contact.UserProperties.Find(PROPERTY_NAME_FATHER_BIRTHDAY, Type.Missing);
43: if (_FatherBirthdayProperty != null)
44: {
45: dtpFatherBirthday.Value = (DateTime)_FatherBirthdayProperty.Value;
46: }
47: else
48: {
49: _FatherBirthdayProperty = _Contact.UserProperties.Add(PROPERTY_NAME_FATHER_BIRTHDAY, Outlook.OlUserPropertyType.olDateTime, Type.Missing, Type.Missing);
50: }
51:
52: // 将这四个控件绑定change事件,只有在修改之后,我们才会将值回写到Contact对应的属性中去
53: tbMotherName.TextChanged += new EventHandler(content_Changed);
54: dtpMotherBirthday.ValueChanged += new EventHandler(content_Changed);
55: tbFatherName.TextChanged += new EventHandler(content_Changed);
56: dtpFatherBirthday.ValueChanged += new EventHandler(content_Changed);
57:
58: // 在Write事件中,把修改的值保存到属性中去
59: _Contact.Write += new Microsoft.Office.Interop.Outlook.ItemEvents_10_WriteEventHandler(contact_Write);
60: }
content_Changed和contact_Write事件
1: void content_Changed(object sender, EventArgs e)
2: {
3: // 有修改时,将_Change置为true
4: _Changed = true;
5: }
6:
7: void contact_Write(ref bool Cancel)
8: {
9: if (_Changed)
10: {
11: // 保存值到属性中去
12: _MotherNameProperty.Value = tbMotherName.Text.Trim();
13: _MotherBirthdayProperty.Value = dtpMotherBirthday.Value;
14: _FatherNameProperty.Value = tbFatherName.Text.Trim();
15: _FatherBirthdayProperty.Value = dtpFatherBirthday.Value;
16: }
17:
18: }
EmployeeFamilyForm_FormRegionClosed事件
1: private void EmployeeFamilyForm_FormRegionClosed(object sender, System.EventArgs e)
2: {
3: // 关闭事件绑定
4: _Contact.Write -= new Microsoft.Office.Interop.Outlook.ItemEvents_10_WriteEventHandler(contact_Write);
5:
6: // 释放对象
7: System.Runtime.InteropServices.Marshal.ReleaseComObject(_Contact);
8: _Contact = null;
9: }
后记
自此我们已经完成了对Outlook联系人的扩展,在本文中我们介绍了Outlook Form Region和UserProperties的使用,基本圆满完成了市场部的需求。话说IT帮他们实现这个需求之后,过了几天市场部的同事又来他们了。因为客户的人数比较多,最好能做一个查询工具希望可以做一个查询工具,来方便他们检索内容。至于如何检索Outlook内部对象的方法,且听下回分解。
最后,本文欢迎转载,但请保留出处,大家如果有问题,可以联系我 justin.tyrael@gmail.com或者到VSTO之路小组中提问。本文所涉及的源代码可以在这里下载。
转载于:https://www.cnblogs.com/izualx/archive/2011/06/12/2079130.html
我的VSTO之路(五):Outlook初步开发之联系人扩展相关推荐
- java 设置年轻代堆大小,[JVM学习之路]五堆(一)堆的内存结构参数设置分代思想内存分配...
[JVM学习之路]五堆(一)堆的内存结构参数设置分代思想内存分配 [JVM学习之路]五.堆(一)堆的内存结构.参数设置.分代思想.内存分配策略及TLAB 一.堆的核心概述 堆的特点: 1.一个jvm实 ...
- 我的LinkedIn领英之路,如何高效开发外贸客户
聪明的外贸人,要掌握并精通一个开发渠道 外贸开发的渠道很多,阿里.谷歌.海关数据.社媒-- 外贸类的培训也很多,线上的培训课,线下的活动与聚会-- 外贸类的信息以及工具也很多,基本都会告诉你一旦拥有别 ...
- 【转】基于Web技术的Outlook Add-ins开发简介
[转]基于Web技术的Outlook Add-ins开发简介 转自: http://www.cnblogs.com/i-love-tech/p/8116989.html 我也是刚刚接触Outlook ...
- apollo local 模式_Java客户端使用指南 - 五、本地开发模式 - 《携程 Apollo v1.4 开发指南》 - 书栈网 · BookStack...
五.本地开发模式 Apollo客户端还支持本地开发模式,这个主要用于当开发环境无法连接Apollo服务器的时候,比如在邮轮.飞机上做相关功能开发. 在本地开发模式下,Apollo只会从本地文件读取配置 ...
- BAT解密:互联网技术发展之路(5)- 开发层技术剖析
BAT解密:互联网技术发展之路(5)- 开发层技术剖析 1. 开发框架 在系列文章的第2篇"BAT解密:互联网技术发展之路(2)- 业务如何驱动技术发展"中我们深入分析了互联网业务 ...
- 本周个人总结(软件的初步开发)
软件初步开发流程 计划: 此软件的初步开发 打开需要两个小时左右的时间. 开发: l 用户通过输入的队伍名称可以查询到自己想知道的内容,可以通过输入,和确定按键来执行功能. l 设计文档:由用户的 ...
- javaweb学习总结(五):Servlet开发(一)
一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...
- Enterprise Library v3 初步开发计划
Tom Hollander在几天前发布了一篇Post,提出了一些Enterprise Library v3(下一个版本的Enterprise Library 具体的名称还没定,暂时先用Enterpri ...
- PROFINET I/O设备初步开发成功发帖庆贺一下
经过将近3个月的奋战,PROFINET I/O设备初步开发成功发帖庆贺一下. 一.PLC挂接分布式机架模块(该分布式机架目前挂接了7个模块) 二.单片机程序 三.GSDML文件
- 54:第五章:开发admin管理服务:7:人脸入库流程;人脸登录流程;浏览器开启视频调试模式(以便能够在本机的不安全域名的情况下,也能去开启摄像头);
说明: (1)本篇博客内容:介绍人脸入库流程,人脸登录流程:人脸入库流程:Chrome开启视频调试模式,以便能够在本机的不安全域名的情况下,也能去开启摄像头: 目录 一:人脸入库流程,人脸登录流程: ...
最新文章
- Solaris下调整opt分区的inode结构
- 隐马尔可夫模型中的Viterbi算法zz
- Nginx负载均衡状态介绍
- 深度学习之卷积神经网络(5)表示学习
- 『设计模式』80年代的人们就已经领悟了设计模式-- 发布者/订阅者模式 (包括发布者/订阅者模式和观察者模式的区别)
- redux进一步优化
- Fastformer:简单又好用的Transformer变体!清华MSRA开源线性复杂度的Fastformer!
- html页面上使用vlc,【JSJQuery】使用VLC在html中播放rtsp视频
- 62 岁的比尔·盖茨当选外籍院士,但却与微软无关
- 开源视频平台:Kaltura
- 固高运动控制卡的使用 1 启动程序以及环境(C#版)
- 并网光伏发电系统(simulink仿真)
- html右键头,右箭头怎么打 word怎么输入向右的箭头
- 互联网产品设计思路参考
- 架构师之殇:一个反对“平台团队”的案例
- SSL-ZYC 邮票
- js Array 标准方法
- nginx-http重定向到https配置
- 小米路由器mini刷老毛子固件遇到的问题
- 销宝:落地页转换率如何提高?
热门文章
- java邮件抄送_JAVA实现邮件抄送,密送,多个附件发送
- niosII处理器与串行D/A转换器接口设计
- Win11磁盘被写保护怎么办?Win11磁盘被写保护解除方法
- css 实现一个尖角_css3如何做尖角标签效果?
- 端到端和非端到端的Embedding,以及embedding质量评估
- python 64式: 第28式、分布式锁与群组管理__3、tooz应用之分布式锁
- 计算机玩游戏重启,为什么我的电脑玩游戏的时候会不定时的自动重启?
- Nature:肠道细菌能够调节果蝇运动行为
- 微信公众号首次关注自动回复图文信息
- 软件开发量评估法之一---德尔菲评估法