引言
阅读本文时需要接受以下观点:
- Windows 桌面版的 Outlook 是最方便编程管理的邮件、人脉、日历、任务系列服务的客户端软件
- Exchange Server 是最佳的该系列服务的后端
- Exchange 协议是最佳的该系列服务的同步协议
本科开始的时候我决定用日历管理我的学术日历。这里有几个考虑:
- 使用反复事件(recurring appointment)是不好的,因为有的时候课程会取消或者换时间(基于教授的安排)。修改一个事件系列(series)里面的特例是非常麻烦的。与之相对,每节课使用单独的事件修改起来会更简单。中国的一些节假日有调休,另一些是略过这一天,所以也会有修改的需要。
- 因为生病或其他原因没有参加的课程,我会把状态从“忙”改成“空闲”并加上注记,这种情况下单独的事件也更方便。Vnox 则把取消的事件放入另一个日历里面,但是一个事件系列只能同属于一个日历,不能单独挪动一个实例(instance),必须删除一个实例再在另一个日历里面加上一个单独的事件。这种复杂的操作非常难正确实施。
- 我需要为一个学期里的每一周都建立一个事件来标明学术周号。
VBA 写的 Outlook 宏
我忘记第一学年的时候我是怎么做的了,可能是手工管理的。至少从 2015 年 9 月开始,我使用 Outlook 对象模型来辅助管理。彼时我用 VBA 写了一个 Outlook 宏来创建课程的事件。你可以在 GitHub Gist 上 查看我的 Outlook 宏。
代码中 AskForTermStartsOn
会交互式要求用户输入学期开始的日期,这个日期 必须是星期一。输入的日期会被存下来,所以不是每次都需要重新输入。另一个方法 AskForCurriculumCalendar
要求用户选择一个日历文件夹来存放日历事件。
重头戏是 CreateCourse
方法。它会先调用两个 AskFor
方法,让配置进来(如果之前已经写过,默认选项是“采用之前的设置”)。接着它提示用户输入课程的名字(对应于事件的标题)、描述(可选,对应于事件的详细信息)和地点(对应于事件的地点),然后要求用户循环输入 1-10,Mon,8:00-9:50 这种格式的字符串(分别表示上课的周数、星期几和时间),输入空字符串(或者点“取消”)来结束循环。接着该方法总结这个课程的信息,并询问用户是否要创建课程的事件,如果用户同意,则创建事件。
还有一个趣闻:在我的 Outlook.com 邮箱切换到 Exchange 之前,同步协议是 Exchange ActiveSync,使用起来有一些乱七八糟的小毛病,比如手工快速创建好几个日历事件会导致同步失败且需要删除 pst/ost 文件才能恢复(分批创建多个日历事件也会,所以当一个学期有超过三个课程的时候就会崩掉),以致于我需要先启用“离线模式”,编辑好日历之后再关闭离线模式,一股脑儿同步上去来避免问题。换到 Exchange 之后自然是大不一样,业界事实标准当然是杠杠的!
在这之前,我尝试写过一个叫做 Curriculum Scheduler 的 app,我还做了很多 UI 实现上的探索,比如:
- 制作日期选择器、错误信息提示器、应用内 toast 通知展示器,并适配高对比度和 Tab 导航
- 实现 Windows 8.1 内置 app 风格的应用栏“…”提示条
- 考虑 DPI 缩放对数学上正确的公式的影响
- 实现隐藏控件用于做焦点接力,这是因为双向绑定中传回视图模型的部分(因此也包括数据验证)是在焦点离开控件之后才发生的,而数据验证结果可以改变“下一个聚焦对象”是谁,因此需要用一个焦点接力器,先允许双向绑定回传,然后移动到恰当的位置(是“完成”按钮,还是“错误信息提示”?)
但是由于一些我当时实在是没时间、没精力、没办法对付的诡异 bug(该 bug 我只能找到工程量足够大的时候的复现方法,无法产生一个足够小的复现),我后来放弃了,走上了使用 Exchange、Outlook 的正道。以下是一些相关微博:
简洁起见,这些链接默认折叠,点击这里显示它们。
你可以折叠下面的链接,点击这里折叠它们。
- 用 PowerPoint 绘制应用 logo
- 日期选择器和折叠的错误提示器
- 高对比度下的日期选择器
- 学期编辑界面
- 日历展示的演示视频
- 用 Live Connect API 操作日历
- 多种尺寸的错误提示器
- 正确绘制焦点框
- 完善的日期选择器 1
- 完善的日期选择器 2
- 完善的日期选择器 3
- 完善的日期选择器 4
- 应用栏提示条
GridView
不同大小的单元格的作弊方法,该方法只能用于当前列一定可以容纳的情况- 比较完善的程序,注意这里提到的夏令时相关的内容是错误的,不同的程序有不同的对待方法,见 Raymond Chen 的这篇文章,在 Outlook 中,日历事件的开始结束时间是具有时区的,在进入或离开夏令时后,事件的“本地时间”不会改变
你可以折叠上述链接,点击这里折叠它们。
一次性的 PowerShell 脚本
2016 年的时候我尝试了一下用 PowerShell,见这里,这个脚本是用来创建周事件的(之前的 Outlook 宏没有实现)。但是由于当时比较懒,也没有什么新的需求,所以就没有实现,继续用的 VBA。
不过后来由于我上了法语课(français),我发现 VBA 对 Unicode 支持得不好,所以还是得用 PowerShell,见 这里 和 这里。
本科后面的时间我就断断续续地用 VBA 和一次性的脚本建立课程。
较完善的 PowerShell 脚本
升学后,我产生的新的需求,而且由于需求会一直存在,所以就用 PowerShell 比较认真地写了一些脚本来管理。总结一下过去遇到的问题和解决思路:
- VBA 的 Unicode 支持差,用 PowerShell 解决
- 之前的脚本都是一次性的,要写一些持久能用的脚本解决
- 之前几乎没用过“课程描述”,所以不用这个了
- 因为退课和选课交叉进行,有的时候要删除好多个事件挺麻烦的,可以用 Exchange 日历的自定义属性记录一些数据来关联课程和日历事件
- 之前 VBA 里输入仅在单周进行的课程不方便,现在支持“打印页数”语法来输入周数
- 之前 VBA 写死了每周从周一开始且仅支持 20 周(清华是这样的),但是 UCSB 里一个学期的周号是 0 到 10 或 1 到 10,且每周从周日开始,新的脚本支持两种模式
- 之前 VBA 写死了事件提示是开始前 15 分钟,是因为在清华骑车 15 分钟总是可以从宿舍到教室,但 UCSB(几乎所有美国大学)的情况和这个不一样,新的脚本支持自定义提醒时间
- 之前 VBA 单个课程必须具有相同的标题和地点,习题课是作为单独的课程呈现的,新的脚本支持把习题课作为课程的一部分(可以重写特定一组课节的标题、地点和提醒时间)
新的脚本托管在我的 GitHub 仓库 PowerShellThingies 里面。发微博臭美肯定 也是少不了的。
总结
下表总结了我实现的那些代码的功能。
方法 > | VBA | PowerShell 一次性 | PowerShell 脚本 |
---|---|---|---|
创建周事件 | 未实现 | 自己实现 | 实现 |
创建课程事件 | 实现 | 自己实现 | 实现 |
周输入支持 | 连续区间、数次添加 | 自己实现 | “打印页数”输入格式 |
周数支持 | 仅支持 20 周(清华) | 自己实现 | 支持任意周编号上下界 |
星期起点 | 星期一 | 自己实现 | 星期一或星期日 |
Unicode | 否 | 是 | 是 |
交互式输入 | 实现 | 自己写代码 | 实现 |
数据保存 | 未实现 | 未实现 | 实现 |
关联事件和课程 | 未实现 | 未实现 | 实现 |
未来说不定还会有新的需求加上来,到时候再看吧。
请启用 JavaScript 来查看由 Disqus 驱动的评论。