65.9K
CodeProject 正在变化。 阅读更多。
Home

WTL 的 Outlook 栏(支持 XP!!)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (16投票s)

2006年5月25日

MIT

10分钟阅读

viewsIcon

66297

downloadIcon

2410

一个带主题的 WTL Outlook Bar(即使没有主题也能工作)。

Sample Image - wtloutbar.gif

记住:

英语/美式英语不是我的母语。我是意大利人。向他人解释事物不是我的职业,我是一个神秘的程序员:-)……我独自生活在一个黑暗的地牢里:-)我有点讽刺,但话说回来,我是一个疯狂的程序员:-)。

引言

我喜欢 Outlook 风格的控件……我非常喜欢它们……我的问题是很难找到一个支持 XP 主题的。所以,有一天(大约一年前),我决定自己做一个……我与糟糕的主题文档作斗争,最终我赢了。这是我胜利的成果……

缺点

它不支持:小图标、按钮上的省略号、拖放、项目的就地编辑、项目排序、项目移动、良好且易于扩展的事件模型、所有者绘制项目、不可按的图标(现在按下的图标会记住它被按下了,并以不同的样式绘制……通常这是 OutBar 的扩展样式,而不是默认样式)、禁用主题部分(OutBar **会**在 W95/W98/NT 等系统上工作)。我甚至不确定它是否能在非默认主题下正确绘制(我用所有默认的 XP 主题尝试过,但我没有 StarDock 或类似程序)。

优点

它重绘速度快(它使用双缓冲技术),它有一个双重接口:基于消息的(`OBM_*`)和基于方法的(你知道的,一个围绕消息的薄包装器,就像 ATL 和 WTL 对 Windows 所做的那样,许多方法都由一行组成:`SendMessage(m_hWnd, OBM_something, lParam, wParam)`),它有一个查找方法(技术上有两个,一个用于文件夹,一个用于项目),它用所有者绘制的滚动条(尝试拉伸它的宽度和长度)和所有者绘制的 ListIcon(从头开始构建……第一个版本使用 ListView,但我与它的滚动作了徒劳的斗争,因为我想要图标水平居中,所以我决定从零开始创建它)绘制得非常华丽,它完全支持键盘和鼠标(箭头、pgup/pgdown/home/end/滚轮),热点项目绘制不同(仅在使用主题时),它支持 UNICODE 和 W64,它在 W4 警告下编译。

不确定之处

它采用 MIT 许可证(简化版 BSD 许可证),它使用许多 Windows 控件(一个用于自身,每个文件夹一个,每个图标一个)。最后一个项目属于“不确定”类别,因为我认为 OutBar 的每个部分都可以单独控制(并且程序可以正确地 `GetFocus()`/`SetFocus()`),并且用户可以在 OutBar 的各个部分之间使用 Tab/Shift-Tab 键进行切换,这是一件好事,但它消耗了许多控件,则是一件坏事。

我该如何使用它?

请记住:此代码已通过 VC++ 8.0/WTL 7.5 测试!!我不想写三页来告诉你,你只需打开 wtlOutBarDemo 项目就可以发现的事情(提示:所有“重要”行都用 `// WTLOUTBAR` 标记,因此你可以轻松搜索它们)。如果你知道如何使用 `CListViewCtrl`,那么你就知道如何使用 `CWtlOutBarImpl`(是的,我“借用”了 ListView 的接口)。最后,最困难的事情可能是创建包含图标的 ImageList。有两种方法可以做到:你可以将所有图标放在单独的 *.ico 文件中,然后一次一个地执行 `LoadIcon`/`AddIcon`,**或者**你可以扮演小小的疯狂科学家,将所有图标合并到一个位图中,然后使用 `CreateFromImage`。小提示:wtlOutBarMDIDemo 是 OutBar 的演示,**而不是** Split+MDI 的演示。我希望它能正常工作,但我不是很确定。拆分窗口和 MDI 框架窗口是复杂的参数,它们就像 WTL 的“未被发现的土地”。

所有的类是什么?

  • CWtlOutBarT,其中 99% 的情况下,`TBase = CWindow`,是 OutBar 的一个薄封装器。它由许多单行方法组成,这些方法只是简单地调用正确的 сообщения。除非你想做一些奇怪的事情,否则你不需要直接使用这个类。
  • CWtlOutBarImplT,其中 99% 的情况下,TBase = CWtlOutBarT。这是“核心”类。你可能会在你的主窗体中的某个地方声明它,它会一直待在那里。如果你从 `CWtlOutBarT` 派生它,你可以轻松地使用它的方法(这样你就可以 `AddFolder`、`AddItem`、`GetSelectedFolder`、`GetSelectedItem` 等)。
  • CMemoryStripDC 是一个实用类。我需要一个可重用的 CMemoryDC,所以我创建了一个(OutBar 是以“条带”绘制的,每个图标及其描述都是一个条带)。

你的 OutBar 有什么奇怪的,有什么神奇的地方吗?

嗯……是的……这个类支持两种样式(它们是“基本”样式,不是扩展样式)。

  • OBS_SHAREIMAGELISTS (与 LVS_SHAREIMAGELISTS 完全相同,所以我**不会**在这里解释……好吧……我会解释的):如果你不使用该样式,OutBar 会认为拥有 ImageList 的所有权,并在 OutBar 销毁时销毁它。如果你使用该样式,OutBar 不会在其生命周期结束时销毁 ImageList。
  • OBS_FORCESELECT:如果你设置此样式,OutBar 将始终有一个选定的项目(除非没有项目)。如果你删除了选定的项目,另一个项目将被选中(以智能的方式……它会首先尝试向下,然后尝试从先前选定的项目向上)。

这个类会抛出三个通知:`OBN_FOLDEROPEN`、`OBN_ITEMSELECT` 和 `OBN_ITEMSELECTAUTO`。

  • OBN_FOLDEROPEN 很清楚:当用户打开一个文件夹时触发。
  • OBN_ITEMSELECT 在用户选择项目时触发(通过点击或使用键盘)。
  • OBN_ITEMSELECTAUTO 在程序选择项目时触发(例如,通过使用 `SetSelectedItem` 或触发 `OBS_FORCESELECT` 的效果)。

嘿,我想要一些文档

你很幸运,你**非常**幸运。我决定学习如何使用 Doxygen,所以我注释了源代码的所有“用户”部分(你知道的,非内部部分)。你可以用 HTML 格式或 CHM 格式(Windows HTML 帮助格式)阅读它……当你查看源代码时,你也可以阅读它(因为注释在源代码中,doxygen 只是提取它们并创建一个易于阅读的文件)……所以,在让你有点困惑之前,我将教你一件事(一件我**在**编写 OutBar 之后才发现的事情)。

m_wtlOutbar.GetHWNDHandle(-1).ModifyStyleEx(0, WS_EX_STATICEDGE);

其中 `m_wtlOutbar` 是 OutBar 的名称。这将使 OutBar 的 ListView 部分具有静态边缘(更具 3D 感)。

嘿,但我有点懒,我真的想要一些例子

好的……我将解释 wtlOutBarDemo 示例……创建一个基于对话框的 WTL 项目。将 *wtloutbar.h* 和 *stringcex.h* 复制到项目文件夹中并添加到项目中。向项目添加一些图标,并给它们一个 ID,例如 `IDI_ICON1`…`IDI_ICONx`。在 *stdafx.h* 中,在 `#define WINVER` 行附近添加以下行:

#define _WIN32_WINNT 0x0501

这是主题支持所必需的。

在 `#include ` 之后,添加

#include <atlcoll.h> 
#include <atltheme.h>
#include <tmschema.h>
#include "wtloutbar.h"

第一个 (atlcoll.h) 是因为我使用了 ATL 集合。第二个 (atltheme.h) 用于主题。第三个 (tmschema.h) 是因为微软决定将主题的基础头文件和包含编写基于主题的应用程序所需的所有“代码”的头文件分开是件好事。第四个 (wtloutbar.h) 是 OutBar 库(它会自动包含 stringcex.h)。

现在,您应该前往项目的属性,在 Linker->Input 页面中,将 `delayimp.lib` 添加到 Additional Dependencies,并将 `uxtheme.dll` 添加到 Delay Loaded DLLs。这样,您的程序将与没有 `ComCtl32.dll` v6 库(用于主题的库……是的……我知道,我写的并不完全正确……这是一种过度简化……所以开枪吧:-))的 pre-XP 系统兼容。

现在,进入 `mainfrm.h` 文件,在 `#pragma once` 行之后,添加

#define IDC_OUTBAR 10000

这将是控件的 ID(是的,10000 是一个随机数;它不应该被任何人使用,而且 ID 是局部的,所以不会有问题,但如果你不喜欢它,**请更改它**!)。然后在 `public:` 行下方,添加

CWtlOutBarImpl m_wtlOutbar;

这就是控件。非常简单。现在,进入 `OnInitDialog(…)`。在 `Return TRUE` 之前,添加

CImageList il; 
il.Create(32, 32, ILC_COLOR8|ILC_MASK, 6, 1); 

CIconHandle ico; 
ATLVERIFY(ico.LoadIcon(IDI_ICON1)); 
ATLVERIFY(il.AddIcon(ico) != -1); 
ATLVERIFY(ico.Detach()); 

ATLVERIFY(ico.LoadIcon(IDI_ICON2)); 
ATLVERIFY(il.AddIcon(ico) != -1); 
ATLVERIFY(ico.Detach());

这将创建一个 ImageList,其中包含六个图标的基本空间(您可以使用不同的值),并且会每次增加一个图标。然后,它将开始加载图标并将它们添加到 ImageList 中。我们正在从 *.exe* 文件中加载图标,因此您在将它们添加到 ImageList 后无需真正地“释放”它们(因此使用了 `ico.Detach()`)。您可以添加更多图标……只需重复最后三行。

RECT rect; 
GetClientRect(&rect); 
rect.right /= 3;

这将计算客户矩形区域(窗口宽度的三分之一)。

ATLVERIFY(m_wtlOutbar.Create(m_hWnd, &rect, NULL, 
          WS_CHILD|WS_VISIBLE|OBS_FORCESELECT, 0, IDC_OUTBAR));

这将创建 OutBar。我们正在使用 `OBS_FORCESELECT` 样式。

m_wtlOutbar.SetImageList(il, OBSIL_NORMAL);

我们设置了 OutBar 的 ImageList。请注意,`OBSIL_NORMAL` 是唯一受支持的图像列表样式。请注意,我们是“局部”创建 ImageList 的(ImageList 的作用域是 `OnInitDialog` 方法)。这没问题,因为 OutBar 将拥有 ImageList 的所有权(我们没有使用 `OBS_SHAREIMAGELIST` 样式)。

ATLVERIFY(m_wtlOutbar.SetFolderCount(10));

我们为 10 个文件夹准备内存(我们不会使用它们)。

ATLVERIFY(m_wtlOutbar.AddFolder(OBFF_TEXT|OBFF_PARAM, _T("[2]"), 3) != -1); 
ATLVERIFY(m_wtlOutbar.InsertFolder(0, OBFF_TEXT|OBFF_PARAM, _T("[1]"), 5) != -1);

我们添加了两个文件夹(技术上,我们添加了一个文件夹,然后将另一个文件夹插入到刚添加的文件夹之前)。我们设置了文件夹的标题和 `PARAM`(3 和 5 是 `PARAM`。`InsertFolder` 的 0 是文件夹将被插入的位置)。

m_wtlOutbar.AddItem(0, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[1-1]"), 1, 10); 
m_wtlOutbar.AddItem(1, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[2-2]"), 2, 20); 
m_wtlOutbar.InsertItem(0, 1, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[1-2]"), 3, 30); 
m_wtlOutbar.InsertItem(0, 2, OBIF_TEXT|OBIF_IMAGE|OBIF_PARAM, _T("[1-3]"), 4, 40);

我们添加了两个项目(每个文件夹一个),然后又插入了两个(技术上,我们将 Insert 用作 Add(技术上,Add 只是 Insert 的一个快捷方式))。我们设置了文本、图像(1、2、3 和 4 是图标编号,基于零),以及 `PARAM`(10、20、30 和 40)。第一个数字(0、1、0、0)是添加项目的文件夹编号。`InsertItem` 方法使用的第二个数字是项目将被插入的位置。

完成!!

现在,你已经创建了 OutBar……你可以尝试编译,然后打开字典查找一些新的骂人话(因为程序中肯定会潜入一些小错误),修正所有错误,然后重试:-)

在示例中,我甚至在 `OnSize` 事件处理程序中使用了这个

m_wtlOutbar.SetWindowPos(NULL, 0, 0, LOWORD(lParam) / 3, 
            HIWORD(lParam), SWP_NOMOVE|SWP_NOZORDER);

(其中 `lParam` 是窗口的新宽度)。这样,OutBar 始终与窗口成比例。现在,你可以添加通知处理程序……将这些添加到 `BEGIN_MSG_MAP()`

NOTIFY_HANDLER(IDC_OUTBAR, OBN_FOLDEROPEN, OnFolderOpen) 
NOTIFY_HANDLER(IDC_OUTBAR, OBN_ITEMSELECT, OnItemSelect) 
NOTIFY_HANDLER(IDC_OUTBAR, OBN_ITEMSELECTAUTO, OnItemSelectAuto)

然后,创建三个通知处理程序。要找出新打开的文件夹,请使用

int iFolder = m_wtlOutbar.GetOpenFolder();

要找出哪个项目被选中,请使用

int iFolder = m_wtlOutbar.GetSelectedFolder() 
int iItem = m_wtlOutbar.GetSelectedItem()

真的完成了!!:-)

历史

  • wtloutbar.h - 1.0 - 2005 年 5 月 - 初始版本(未发布)。
  • wtloutbar.h - 1.1 - 2006 年 5 月 - 小改进,添加了注释。
  • wtloutbar.h - 1.11 - 2006 年 5 月 - 修复了 `WM_DESTROY`/`WM_NCDESTROY` 处理程序中的错误。将 `CMemoryStripDC` 放入另一个头文件中。
  • wtloutbar.h - 1.12 - 2006 年 6 月 - 修正了小的内存泄漏(HRGN 未释放)。更改了销毁时释放内存的方式。
  • wtloutbar.h - 1.12R2 - 2007 年 11 月 - 修复了 MDI 演示。最终,它似乎能正常工作了。感谢 Nenad 提供了好主意。

许可证

我是一个 BSD 爱好者,因此我将根据 MIT 许可证(一个简化的 BSD 许可证)许可此库。

版权所有 (c) 2005, 2006 Massimiliano Alberti xanatos(at)geocities.com

特此免费授予任何获得本软件及相关文档文件(“软件”)副本的人,不受限制地处理本软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,并允许接收者在符合以下条件的情况下获得本软件:

上述版权声明和本许可声明应包含在软件的所有副本或重要部分中。

本软件按“原样”提供,不提供任何明示或暗示的担保,包括但不限于适销性、特定用途适用性和不侵权的担保。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权行为还是其他方面,均由本软件或本软件的使用或其他交易引起或与之相关。

© . All rights reserved.