WTL 中的所有者绘制 ListViews






4.65/5 (11投票s)
2004 年 6 月 27 日
3分钟阅读

73035

4382
在 WTL 中处理 owner drawn listview 控件的概述。
引言
Owner drawing 控件是一项很好的技能。 易于掌握,立竿见影。 妥善保管它,因为如果使用得当,它可以使您的应用程序看起来更专业,而不会显得您只是过于努力并搞砸了。 通过学习使用它,您将挥舞着 Microsoft Word 所拥有的宽剑,而 Notepad 则没有。 让我们继续。
本文旨在让您快速了解基础知识。
背景
owner drawn listview 控件是一个普通的 report view list view 控件,它也会生成 4 个 owner draw 消息。它希望您来绘制。
WM_DRAWITEM
(当每个项目需要绘制时调用)WM_MEASUREITEM
(请求行高,在调整大小时调用)WM_COMPAREITEM
(用于排序)WM_DELETEITEM
(当每个项目被删除时调用)
使用代码
分解
使用 WTL 向导,我创建了一个基于对话框的项目,没什么特别的。 我添加了一个派生自 COwnerDrawnListViewCtrl
的新类。
class CDemoOwnerDrawnListViewCtrl : public COwnerDrawnListViewCtrl<CDemoOwnerDrawnListViewCtrl> { public: BEGIN_MSG_MAP(CDemoOwnerDrawnListViewCtrl) CHAIN_MSG_MAP(COwnerDrawnListViewCtrl<CDemoOwnerDrawnListViewCtrl>) END_MSG_MAP() void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); };
对于 DrawItem
,您在其上进行绘制的 dc 位于 lpDrawItemStruct->hDC
中,当前项目的矩形位于 lpDrawItemStruct->rcItem
中。
对于 MeasureItem
,您所需要做的就是将 lpMeasureItemStruct->itemHeight
设置为所需的高度。
您的控件已完成。
我添加到 Dialog 类的所有行都附加了 "// ADDED
",以便您可以在文件中查找。 使用您的控件的基础是
- 通过将 ListView 控件子类化为 "Owner Drawn Fixed" 属性设置为 true 来创建您的
COwnerDrawnListViewCtrl
派生控件。 如果您将使用WM_MEASUREITEM
(见下文),请调用ForceMeasureItemMessage()
。m_lvwDemoListViewCtrl.SubclassWindow(GetDlgItem(IDC_LIST1)); m_lvwDemoListViewCtrl.ForceMeasureItemMessage();
- 确保添加一些列 (
AddColumn()
)。 - 对话框需要将通知消息反射回控件。 您可以通过在父对话框的消息映射中添加
REFLECT_NOTIFICATIONS()
来处理此问题。
您现在应该没问题了。
详细说明
COwnerDrawnTaskListView
是一个非常简单的类,只有 3 个函数,它本身派生自 CWindowImpl
和 COwnerDraw
。
void ForceMeasureItemMessage(); void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/) {}; void GetCellRect(int header_column, const CRect& item_rect, CRect& cell_rect);
在 COwnerDrawnTaskListView
内部,我们 CHAIN_MSG_MAP_ALT(COwnerDraw<TBase>, 1)
因为我们正在处理反射消息 (OCM_XXXX
)。 如果我们是父级,那么我们只会 CHAIN_MSG_MAP(COwnerDraw<TBase>)
。
需要 ForceMeasureItemMessage()
才能接收 WM_MEASUREITEM
消息。 问题是您无法足够快地对窗口进行子类化以最初接收它,因为它是在创建之前发送的。 您捕获它的唯一方法是您 .Create()
或 .CreateEx()
它。 在这种情况下,您不需要 ForceMeasureItemMessage()
。 ForceMeasureItemMessage()
的魔力在于将控件向上移动一点,从而生成 WM_MEASUREITEM
消息。 当 ForceMeasureItemMessage()
将其移回时,您会收到另一个消息。
需要空的 DeleteItem()
函数来修复 WTL 中发生的一个含糊不清的访问问题。 如果您未定义它,则会发生的情况是,OnDeleteItem()
将要调用 DeleteItem()
,而这是含糊不清的。 您获得了 WTL::CListViewCtrlT<ATL::CWindow>::DeleteItem()
和 WTL::COwnerDraw<T>::DeleteItem()
。 由于它们位于不同的 MI 分支中,因此它们不在彼此的重载范围内。 我们空的 DeleteItem()
函数(您可以使用您的函数将其隐藏在层次结构中)会隐藏这两个函数,从而解决了歧义性。
GetCellRect()
是一个返回“单元格”或子项的坐标在行内的 CRect
的函数,它简化了绘制。
历史
2004 年 6 月 27 日星期日 - 创建。