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

WTL 中的所有者绘制 ListViews

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (11投票s)

2004 年 6 月 27 日

3分钟阅读

viewsIcon

73035

downloadIcon

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 个函数,它本身派生自 CWindowImplCOwnerDraw

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 日星期日 - 创建。

© . All rights reserved.