日历插件 for .dan.g.'s ToDoList - 10个简单步骤™






4.99/5 (61投票s)
ToDoList 的日历用户界面扩展组件,为您提供任务的时间线视图
(当前版本:1.0 - 发布于 2008年6月6日)

引言
本文旨在描述如何为 .dan.g.'s 的 ToDoList 创建一个日历插件。它可能对您有用,原因如下:
- 您可能只想下载 DLL 并获得一个有用的任务列表日历视图。
- 您可能想更进一步,编写自己的插件。
如果您是出于原因 #1 而来,并且害怕原因 #2,请不要担心。继续阅读 - 这很简单!
背景
当我开始使用 .dan.g. 强大的 ToDoList 时,它让我重新思考了我处理任务的方式。以前,我不得不尝试实际记住(哎呀)每件需要完成的小事,然后才能认为一项任务已完全完成。现在说实话,我的记忆力比金鱼还差,所以找到 ToDoList 简直是天赐的礼物。我现在将我所有的任务递归地分解成无数个子任务,我的记忆力不再需要(让我有精力去关注其他同样重要的事情,比如我最喜欢的 Bill Hicks 的笑话,或者《黄金城历险记》的主题曲。“啊啊啊啊啊,总有一天我们会找到黄金城……”——真是太棒了!)。
拥有大量任务和子任务会带来一个问题,因为很难弄清楚您在计划进度上是超前/落后多少(毕竟,这些是您的老板关心的。弄错了可能会导致您再次光顾就业中心。)。因此,我开始花很多时间为我的所有任务设置开始日期和截止日期,但这还不够。无论开始/截止日期多么准确/最新,我都无法在脑海中可视化我的进度。我想要的是一个对计划进度的快速概览,一个项目可视化。
我需要的是 ToDoList 本身内部的一个日历式视图。当然,我可以尝试以某种方式导出我的数据并将其导入另一个程序,但这有什么乐趣呢?这已经在 .dan.g. 自带的 ToDoList 中了,所以我决定帮助他。这是我能做的最少的。并且他给了我关于如何编写自己的 ToDoList 日历插件的广泛建议。
日历概览
文件 CalendarExt.dll 需要放在 ToDoList 目录下.
可以通过 ToDoList 的 [工具->日历] 菜单项激活日历。这将显示 **活动任务列表** 的无模式日历窗口(切换任务列表将相应地隐藏/显示日历窗口)。日历窗口分为两个窗格
- 左窗格 =
MiniCalendar
- 右窗格 =
BigCalendar
MiniCalendar
MiniCalendar
窗格最初包含当前月份和下个月。如果窗口足够大,它还将包含下个月。- 单击第一个月的左/右滚动箭头将分别移至上一个月/下一个月。
- 单击月份标题将显示月份列表,您可以从中选择一个月份跳转。
- 单击
MiniCalendar
中的日期将跳转到BigCalendar
中的该日期。 - 单击“今天”菜单项将在两个日历中跳转到该日期。
BigCalendar
BigCalendar
窗格最初包含今天日期周围 6 周内的每个日期。BigCalendar
中的日期包含活动任务列表中开始/截止日期等于该日期的所有任务。- 开始日期任务显示为绿色标记。
- 截止日期任务显示为红色标记。
- 包含一个或多个任务的任何日期将在两个日历中以黄色高亮显示。
用户操作
- 可以使用 [视图->迷你日历] 菜单项隐藏/显示
MiniCalendar
窗格。 - 可以使用滚动条、鼠标滚轮、箭头键和 PageUp/PageDown 来操作
BigCalendar
。 - 单击
BigCalendar
中的任务(或使用箭头键选择并按 Return)将跳转到活动任务列表中的该任务。 - 重命名活动任务列表中的任务将自动更新任务在
BigCalendar
中的名称。 - 在活动任务列表中更改任务的开始/截止日期将自动移动任务在
BigCalendar
中的位置。
技术部分
BigCalendar
和 MiniCalendar
是我非常慷慨地从 The Code Project 的其他文章中“偷”来的(这是正确的词吗?)第三方类。如果我使用别人的类,我喜欢花一些时间来弄清楚它在做什么以及为什么这样做。我喜欢用我自己的风格重写代码,这样我就可以 (a) 更好地理解它,(b) 更容易调试它。我不需要的类的任何部分都会被剔除。这就是我用在日历中的以下三个类所做的事情:
- 右窗格(
BigCalendar
)是 Frederic Rezeau 创作的一个漂亮的自定义绘制日历。我对这个类所做的主要更改是使用每个日期中的CListBox
,而不是直接将任务绘制为文本。 - 左窗格(
MiniCalendar
)是 Matt Gullett 制作的一个很酷的 Microsoft Outlook 风格的迷你日历。 BigCalendar
中的CListBox
是 Ali Rafiee 的CTransparentListBox
。我还为这个类添加了工具提示支持。
ToDoList 的插件架构
ToDoList 使用插件来提供多种方式来实现对主应用程序的不同定制。已经有几种类型的插件在使用中,包括加密、自定义注释、任务导入/导出和 UI 扩展。使用插件架构的好处包括:
- 使不同功能的实现保持独立,从而使所有相关方的维护都大大简化。
- 避免不必要地增加核心应用程序的大小和复杂性。
- 使得第三方更容易创建自己的插件,而不是编辑(甚至可能破坏)核心应用程序的代码。
- 通过删除 DLL,可以删除一整块不必要的功能。
ToDoList 的所有插件都采用相同的方式构建:插件驻留在 DLL 中并导出一个(或多个)C 函数(它们的名称是未修饰的)。其中一个导出的函数通常是 CreateXXX()
函数,它返回一个指向纯虚拟接口(类)的指针,ToDoList 然后使用该接口与插件进行通信。对于 UI 扩展插件,此函数称为 CreateUIExtensionInterface()
。
虽然对纯虚拟接口的全面讨论超出了本文的范围,但它们本质上定义了一个插件承诺支持的契约,而无需两个组件之间进行显式绑定。您可以说它们是调用已加载 DLL 上的 GetProcAddress
的 C++ 等效项。纯虚拟接口是 COM 的核心,并且是实现组件接口和实现分离的成熟方法。
- 注意 1:ToDoList 避免使用 COM 是因为其注册要求,这将阻止插件在 USB 驱动器上运行。
- 注意 2:ToDoList 通常在启动时加载插件,然后在会话期间一直保留接口指针。
对于 UI 扩展插件,ToDoList 提供了两个接口,都位于 Shared\IUIExtension.h
IUIExtension
接口是一个高级接口,它允许 ToDoList 创建插件窗口,并从插件获取菜单文本和图标信息,以便为每个插件在 [工具] 菜单中添加菜单项。IUIExtensionWindow
接口代表用户实际交互的窗口。这里是插件的核心所在,它响应来自 ToDoList 的编辑通知,并在用户单击某项时选择性地将选择事件发送回 ToDoList。
创建日历插件
以下是我创建基本工作日历插件所用的 **10 个简单步骤™**

第一阶段:创建项目,创建类,创建接口存根
- 创建一个新的 C++ DLL 项目,名为
CalendarExt
。更改链接器设置,使 CalendarExt.dll 构建到与 ToDoList.exe 相同的文件夹中。 - 创建两个新类,
CCalendarExtApp
和CCalendarFrameWnd
,分别派生自IUIExtension
和IUIExtensionWindow
,其中包含所有纯虚拟函数的存根。 - 创建一个名为
CreateUIExtensionInterface
的导出函数(也在 Shared\IUIExtension.h 中定义),它只返回一个指向新创建的(或static
)CCalendarExtApp
实例的指针。 - 实现
CCalendarExtApp::GetMenuText
函数返回“日历”,并实现CCalendarExtApp::GetIcon()
函数返回一个合适的图标。 - 编译项目并重新启动 ToDoList.exe。通过检查 [工具] 菜单中是否有名为 [日历] 的菜单项,以及是否有正确的图标,来确认 ToDoList 是否找到该插件。注意:此时,选择菜单项将无效,因为它尚未连接。
第二阶段:实现接口函数,创建窗口
- 实现
CCalendarFrameWnd::Create()
来创建一个适合用户交互的窗口。我选择将CCalendarFrameWnd
派生自CFrameWnd
,但您可以使用无模式CDialog
、CPropertySheet
、CView
或其他任何窗口。 - 实现
CCalendarExtApp::CreateExtensionWindow()
以返回一个指向新创建的CCalendarFrameWnd
实例的指针,并调用该窗口的Create
函数。 - 重新编译项目并重新启动 ToDoList.exe。检查选择 [工具->日历] 菜单项是否显示了窗口实例。此时,检查在多个任务列表之间切换是否正确地隐藏和显示了正确的窗口,因为每个打开的任务列表都有其自己的插件窗口实例。切换任务列表应关闭当前任务列表的插件窗口,并打开新任务列表的插件窗口(如果存在)。
注意:ToDoList 负责插件窗口的所有显示和隐藏;插件的责任只是合理地实现纯虚拟接口。


第三阶段:不太容易的部分 - 实现窗口中将显示的内容,响应任务列表更新事件
- 修改
CCalendarFrameWnd
类,在其客户端区域显示CBigCalendarCtrl
和CMiniCalendarCtrl
窗口,并响应大小调整事件。 - 实现插件和任务列表之间的双向通信(如果需要)。这包括:
- 添加代码以响应活动任务列表中的编辑事件。当任务的属性发生更改时,会调用
CCalendarFrameWnd::Update()
。从这里,我用更新的信息更新我的CCalendarData
对象(一个缓存活动任务列表所有任务的类)。然后,我调用CBigCalendarCtrl
和CMiniCalendarCtrl
对象中的TasklistUpdated
函数,告诉它们使用最新数据更新其视图。 - 添加代码以从插件通知活动任务列表。在我的例子中,当用户单击日历中的任务时,我会将
WM_TDCM_TASKLINK
消息发送到主 ToDoList 应用程序窗口,并附带单击任务的 ID。ToDoList 会解释此消息并选择活动任务列表中的指定任务。
- 添加代码以响应活动任务列表中的编辑事件。当任务的属性发生更改时,会调用
就是这样!我不会用更多细节来烦扰您关于 Big/MiniCalendars 如何工作。您没时间了!您还在等什么?**现在就去写一个插件吧!**
此版本的 ToDoList 日历插件可与版本 5.4 或更高版本配合使用。请留意此页面以获取更新。所有评论、错误、抱怨或建议都欢迎。
致谢
非常感谢我“借用”的三类的作者,他们是:
- Frederic Rezeau
- Matt Gullett
- Ali Rafiee
当然,也必须非常感谢 .dan.g.,在我看来,他是开源代码作者的灵感。我从来没有遇到过如此友善的人,他能如此迅速地处理错误/请求,并对他的用户群充满热情。Dan 对我很有耐心,并在插件架构和日历建议方面给了我很多帮助,帮助我修复了一些糟糕的错误,并且为本文提供了大量信息!
未来功能
- 任务着色以匹配活动任务列表中的颜色
- 将任务从一个日期拖放到另一个日期
- 任务上的弹出菜单
- 额外的键盘处理,例如使用 Delete 键删除任务
- 打印窗口内容(打印到打印机和文件)
- 添加开始和结束日期之间的中间日期(以及完成日期?)
- 添加 TDL 5.5 的截止时间功能
历史
- 1.0 alpha (2008年1月12日)
- 第一版
- 1.0 alpha.2 (2008年1月16日)
- 新增:Esc 键关闭日历窗口
- 新增:任务工具提示
- 新增:标题栏上的最大化按钮
- 修复:日单元格中的非 ASCII 字符支持
- 1.0 alpha.3 (2008年2月12日)
- 新增:显示/隐藏周末 - 使用 [视图->周末] 菜单
- 新增:记住 [视图->周末] 和 [视图->迷你日历] 设置(在 ToDoList.ini 文件中)
- 修复:第二次打开日历窗口后,按键无效
- 修复:在一周的第一天现在根据当前区域设置设置,在 MiniCalendar 和 BigCalendar 中
- 1.0 alpha.4 (2008年3月9日)
- 新增:保存窗口位置,包括最大化状态
- 新增:选择显示的周数 - 使用 [视图->周数] 菜单
- 新增:选择已完成任务的格式 - 使用 [视图->已完成任务...] 菜单
- 新增:在日历窗口标题栏中添加关联任务列表的名称(需要 TDL 版本 5.4.9 或更高版本)
- 修复:在主任务列表中编辑任务的注释窗格时,日历闪烁
- 1.0 alpha.5 (2008年4月30日)
- 新增:添加状态栏以显示所选任务的其他信息 - 使用 [视图->状态栏] 菜单
- 新增:工具提示现在显示悬停项的完整路径
- 新增:更改任务双击行为为单击 - 添加了手形光标以使其更明显
- 新增:MiniCalendar 中前一个月/后一个月的日期现在显示为灰色
- 新增:用“转到今天”菜单项替换了“今天”按钮
- 新增:在 MiniCalendar 上鼠标滚轮现在滚动 BigCalendar
- 修复:在超时后消失的工具提示不会重新出现
- 修复:BigCalendar 和 MiniCalendar 之间的字体统一
- 修复:现在 BigCalendar 中以非英语语言显示正确的星期几
- 修复:标记父任务为完成时更新子任务
- 修复:可以在 BigCalendar 中右键单击选择跨越多个单元格的多个任务
- 1.0 (2008年6月6日)
- 新增:添加了自动更新功能 - 使用 [检查更新...] 菜单
- 修复:显示非常长的工具提示时发生 GPF 错误
- 修复:从另一个应用程序切换回日历窗口后焦点丢失
- 修复:使用 MiniCalendar 月份选择器后焦点丢失
- 修复:如果月份的第 1 天隐藏在周末,则 BigCalendar 中的月份名称不会显示
- 修复:细微的用户界面改进