WTL 图标编辑






4.72/5 (13投票s)
2004年9月28日
4分钟阅读

66433

2827
一篇关于带图标的 WTL 编辑控件的文章。
- 下载 WTL 演示项目 - 62.9 Kb
- 下载 WTL 版本源代码 - 3.24 Kb
- 下载 Win32 版本源代码 - 6.66 Kb
- 下载使用 Win32 版本的 MFC 演示项目 - 30.2 Kb
引言
在开发 MS Internet Explorer 插件(使用 Browser Helper Object)时,我想要一个带图标的编辑控件,类似于 Internet Explorer 的地址栏。CodeProject 上有一篇关于带图标编辑控件的文章,它是用 MFC 编写的(由 Johan Rosengren 撰写:Controls-in-controls: An edit box with an icon)。他做得相当不错,但有几个地方不太令我满意,因此我开始实现自己的控件,现在它就在这里了!
背景
- 编辑控件中的“格式化矩形”。
格式化矩形是系统为格式化窗口矩形中显示的文本而维护的一个构造。换句话说,通过更改格式化矩形,可以限制编辑控件中文本的绘制区域。这是 Johan Rosengren 的实现和我自己实现图标编辑的主要思路。格式化矩形将被设置为这样一种方式,以便为指定的图标绘制保留空间。
有三个与设置/获取格式化矩形相关的编辑消息。
EM_GETRECT
、EM_SETRECT
和 EM_SETRECTNP
。顾名思义,我们可以通过向任何编辑控件发送 EM_GETRECT
来获取格式化矩形,但只能通过发送 EM_SETRECT
或 EM_SETRECTNP
来为多行编辑控件(具有 ES_MULTILINE
样式的编辑控件)设置它。不幸的是,我们不能使用这些消息来为单行编辑控件设置格式化矩形。
在我研究编辑控件的 TitleTip 时,我偶然发现还有一个编辑消息可以更改编辑控件的格式化矩形。那就是 EM_SETMARGIN
消息,无论是否设置了 ES_MULTILINE
样式,它都能正常工作。这是 Johan Rosengren 的实现和我实现之间的第一个区别。
为了在编辑控件中绘制图标,我不想使用编辑控件本身之外的另一个窗口。因此,我花了一些时间研究默认编辑控件是如何以及何时绘制自身的,而不是创建一个用于绘制图标的静态窗口。与常规的 Windows 绘图机制不同,编辑控件不依赖 WM_ERASEBKGND
来擦除背景,它不仅在接收 WM_PAINT
时绘制文本及其背景,还在**某些其他消息**时绘制。
通过快速的互联网搜索,我发现 WM_CHAR
可能是**某些其他消息**的第一个候选。它看起来效果很好,直到我发现使用箭头键同时按住 SHIFT 键选择编辑控件中的文本时,图标会消失。经过进一步的实验和消息监视,我决定拦截并处理 WM_GETDLGCODE
,它效果奇佳!至少,我当时是这么认为的!:P
好吧,它在 Window 2000 机器上运行得非常好。但当我尝试在 Window XP 机器上使用我的控件时,如果应用程序使用的是较新版本的通用控件(版本 6),通过拖动鼠标左键在编辑控件中选择文本时,问题又出现了,因此我需要拦截并处理 WM_MOUSEMOVE
消息,尤其是在按住鼠标左键的情况下。
老实说,我弄不清楚编辑控件究竟是如何以及何时绘制自身的,而每当出现图标绘制问题时,我只能选择添加额外的消息处理程序。如果有人有更好的想法或可以告诉我应该处理哪个消息,请给我留言。
使用代码
使用我的控件非常简单。首先,在你的源代码中包含 "IconEdit.h",然后子类化编辑控件并设置你选择的图标(HICON
)。
#include "iconedit.h" using codeproject::CIconEdit; class CMainDlg : public CDialogImpl<CMainDlg> { public: enum { IDD = IDD_MAINDLG }; BEGIN_MSG_MAP(CMainDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) ... END_MSG_MAP() protected: CIconEdit c_edTest; public: LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // subclass edit control c_edTest.SubclassWindow(GetDlgItem(IDC_ED_TEST)); // set the icon c_edTest.SetIcon(IDI_MY_ICON); ... return TRUE; } };
你可以从 CIconEdit
子类化你自己的编辑控件。
#include "iconedit.h" using codeproject::CIconEdit; class CIconEditBlack : public CIconEdit { typedef CIconEditBlack thisClass; typedef CIconEdit baseClass; BEGIN_MSG_MAP(thisClass) MESSAGE_HANDLER(OCM_CTLCOLOREDIT, OnCtlColorEdit) DEFAULT_REFLECTION_HANDLER() CHAIN_MSG_MAP(baseClass) END_MSG_MAP() LRESULT OnCtlColorEdit(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { uMsg; ATLASSERT(OCM_CTLCOLOREDIT == uMsg); CDCHandle dc((HDC)wParam); dc.SetTextColor(RGB(255, 255, 255)); dc.SetBkColor(RGB(0, 0, 0)); return (LRESULT)(HBRUSH)::GetStockObject(BLACK_BRUSH); } }; class CMainDlg : public CDialogImpl<CMainDlg> { public: enum { IDD = IDD_MAINDLG }; BEGIN_MSG_MAP(CMainDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) ... REFLECT_NOTIFICATIONS() END_MSG_MAP() protected: CIconEditBlack c_edBlack; public: LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // Subclass edit contol c_edBlack.SubclassWindow(GetDlgItem(IDC_ED_BLACK)); // Set the Icon for IDC_ED_BLACK c_edBlack.SetIcon(IDR_MAINFRAME); ... return TRUE; } };
公共函数
BOOL SubclassWindow(HWND hWnd);
子类化必要的消息并为图标绘制预留空间。(默认
margin.cx
= 2,margin.cy
= 0)。HICON SetIcon(ATL::_U_STRINGorID icon, HINSTANCE hResourceInstance = NULL); 或者 HICON SetIcon(HICON hIcon);
设置要在编辑控件中绘制的图标资源(
HICON
)。图标将使用::DrawIconEx()
Win32 API 函数进行绘制。void SetIconMargins(USHORT cx, USHORT cy = 0); 或者 void SetIconMargins(LPSIZE szMargins);
设置图标绘制区域的边距。请参考下图
void GetIconMargins(LPSIZE szMargins);
获取图标绘制区域的边距。
void ShowIcon(BOOL bShow = TRUE);
确定是否显示指定的图标。
历史
版本 1.02 - 2004/10/26
- 添加了
UnsubclassWindow();
。 - 当编辑控件被禁用时,它会绘制禁用的图标。
- 它适用于多行编辑控件(
ES_MULTILINE
样式),即使该控件最初并非为此设计。
版本 1.01 - 2004/09/29
- 移除了
EM_GETRECT
和EM_GETMARGINS
消息处理程序。 - 修改了
EM_SETRECT
、EM_SETRECTNP
和EM_SETMARGINS
消息处理程序。 - 添加了
GetIconMargins()
函数。 - 更新了 WTL 演示项目,并添加了混合类(用于对话框)以支持编辑控件的标题提示。(有关详细信息,请参阅
CTitleTipEdit
类和CMainDlg
类)。
新上传 - 2004/09/28
- 添加了 Win32 版本源代码(无需 WTL/ATL/MFC)以及演示项目,展示如何在 MFC 中使用它。(注意:Win32 版本公共函数名已更改,以避免与
CWnd
函数名冲突)。
版本 1.0 - 2004/09/27
- 在 CodeProject 上首次发布。