Windows Api自绘制ListView控件记录

浏览:1176次阅读
没有评论

提醒:本文最后更新于 2021-09-26 18:22,文中所关联的信息可能已发生改变,请知悉!

 原理
自给制 ListView 控件 (基础第一篇)
首先需要了解 Windows 消息编程的一些基本内容.

Windows 操作系统是消息驱动 (或称为事件驱动)

例如:
1. 用户单击了应用程序的菜单!
2.Windows 就像用一个射像头监视你的一举一动. 所以它拍到了你单击了应用程序的菜单.
3.Windows 告诉应用程序: 刚才有个用户单击了菜单, 该怎么办?(通过消息来传递)
4. 应用程序嘿嘿两下, 收到我来搞定.

消息的种类:
1.(系统消息)
Windows 利用系统定义的消息控制应用程序 (Windows 与应用程序通信)
如 LB: 列表框控制消息 SBM: 滚动条控制消息 WM: 通用窗口消息

2. 通知消息 (从子窗口包括控件发送至它们的父窗口的消息)
Win32 使用 WM_NOTIFY 消息通知它们的父窗口, 如鼠标点击, 控件背景重绘事件等等
在最初的 Windows 3.x 中不存在 WM_NOTIFY. 那么用什么通知呢?
使用一个简单的 WM_COMMAND 消息来通知
WM_COMMAND 结构
{code( 放通知码)
     lParam(放控件句柄)
     wParam(放控件 ID)
}
这样一来 lParam,wParam 都被填满了, 没有额外的空间来传递一些其它消息.
例如: 鼠标按下的位置和时间. 为了克服这个困难 windows3.x 提出了一个比较低级的解决策略.
那就是给一些消息添加附加消息. 最为明显的就是控件自画用到的 DRAWITEMSTRUCT
这个结构包含 9 个内容. 几乎你需要控件的信息都给你提供了. 为什么说它比较低级呢?
因为不同的消息附加的内容不同, 结果就是一盘散沙. 非常混乱.

在 Win32 中,MS 提出了解个更好的解决安案: 引进 NMHDR 结构. 这个结构的引进使得消息统一起来
NMHDR
{
    HWnd hWndFrom; 相当于原 WM_COMMAND 传递方式的 lParam 放控件句柄
    UINT idFrom; 相当于原 WM_COMMAND 传递方式的 wParam 放控件 ID
    UINT code; 相当于原 WM_COMMAND 传递方式的 Notify code 通知码
}

现在来说下 WM_NOTIFY 相对于 WM_COMMAND 有什么特别之处
{
    code; 一个通过码
    wParam; 控件 ID, 但一般不使用它
    lParam; 指向 NMHDR 结构体的指针
    某些控件可能会发送更大的结构体, 它们的第一个成员必须是 NMHDR 这样我们可方便的转换 
}

比如自绘制传递的通常是 NMCUSTOMDRAW 结构体
typedef struct{
    NMHDR hdr;
    DWORD dwDrawStage;
} NMCUSTOMDRAW
那么指向 NMCUSTOMDRAW 的指针当然也指向了 NMHDR 了. 所以二者的转化就成为了可能.

3. 反射消息 (自己生的孩子自己养, 自已发的消息自已处理)
在 Windows 里面,子控件经常向父控件发送消息,例如很多子控件要绘制自己的背景,就可能向父窗口发送消息 WM_CTLCOLOR。对于从子控件发来的消息,父控件有可能在处理之前,把消息返还给子控件处理,这样消息看起来就想是从父窗口反射回来一样,故此得名:消息反射
WM_COMMAND OCM_COMMAND
WM_CTLCOLORBTN OCM_CTLCOLORBTN
WM_CTLCOLOREDIT OCM_CTLCOLOREDIT
WM_CTLCOLORDLG OCM_CTLCOLORDLG
WM_CTLCOLORLISTBOX OCM_CTLCOLORLISTBOX
WM_CTLCOLORSCROLLBAR OCM_CTLCOLORSCROLLBAR
WM_CTLCOLORSTATIC OCM_CTLCOLORSTATIC
WM_CTLCOLOR OCM_CTLCOLOR
WM_DRAWITEM OCM_DRAWITEM
WM_MEASUREITEM OCM_MEASUREITEM
WM_DELETEITEM OCM_DELETEITEM
WM_VKEYTOITEM OCM_VKEYTOITEM
WM_CHARTOITEM OCM_CHARTOITEM
WM_COMPAREITEM OCM_COMPAREITEM
WM_HSCROLL OCM_HSCROLL
WM_VSCROLL OCM_VSCROLL
WM_PARENTNOTIFY OCM_PARENTNOTIFY
WM_NOTIFY OCM_NOTIFY
你会发现一一对应.OCM_* 表示反射的消息
OCM_NOTIFY = OCM_BASE + WM_NOTIFY
消息反射的原理是将发送父窗体的消息加上 OCM_BASE 在反还给子窗体.
如果子窗体认识的话会减去 OCM_BASE



结构体
自绘制当然少不了好好认识下 NMCUSTOMDRAW 这个结构体了
c# 中需要自己定义所有结构体

struct NMCUSTOMDRAW
{
    NMHDR hdr;
    int dwDrawStage
}
public struct NMHDR
{
   public IntPtr hwndFrom;
   public int idFrom;
   public int code;
}

public struct RECT
{
   public int left;
   public int top;
   public int right;
   public int bottom;
}

public struct NMCUSTOMDRAW
{
   public NMHDR hdr;
   public int dwDrawStage;// 当前绘制状态 (***)
   public IntPtr hdc;// 绘制操作所使用的设备环境
   public RECT rc;// 被绘制的矩形区域
   public int dwItemSpec;// 列表项的索引
   public int uItemState;// 当前列表项的状态
   public int lItemlParam;// 当前列表项的绑定数据
}


dwDrawStage 当前绘制状态 取值及含义
{
    CDDS_POSTERASE 擦除循环结束
    CDDS_POSTPAINT 绘制循环结束
    CDDS_PREERASE 准备开始擦除循环
    CDDS_PREPAINT 准备开始绘制循环
    CDDS_ITEM 指定 dwItemSpec,uItemState,lItemlParam 参数有效

    CDDS_ITEMPOSTERASE 列表项擦除结束
    CDDS_ITEMPOSTPAINT 列表项绘制结束
    CDDS_ITEMPREERASE 列表项准备开始列表项擦除
    CDDS_ITEMPREPAINT 准备开始列表项绘制
    CDDS_SUBITEM 指定列表子项
}
常用 CDDS_PREPAINT 与 CDDS_ITEMPREPAINT = CDDS_ITEM | CDDS_PREPAINT

uItemState 当前列表项的状态 取值及含义
{
    CDIS_CHECKED 标记状态 
    CDIS_DEFAULT 默认状态
    CDIS_DISABLED 禁止状态
    CDIS_FOCUS 焦点状态
    CDIS_GRAYED 灰化状态
    CDIS_SELECTED 选中状态
    CDIS_HOTLIGHT 热点状态
    CDIS_INDETERMINATE 不定状态
    CDIS_MARKED 标注状态
}

Message.Result 指向状态值的指针,指定系统后续操作,依赖于 dwDrawStage:当 dwDrawStage 为 CDDS_PREPAINT
Message.Result 取
{
    CDRF_DODEFAULT 默认操作,即系统在列表项绘制循环过程不再发送 NM_CUSTOMDRAW。CDRF_NOTIFYITEMDRAW 指定列表项绘制前后发送消息。CDRF_NOTIFYPOSTERASE 列表项擦除结束时发送消息。CDRF_NOTIFYPOSTPAINT 列表项绘制结束时发送消息。}

当 dwDrawStage 为 CDDS_ITEMPREPAINT
Message.Result 取
{    
    CDRF_NEWFONT 指定后续操作采用应用中指定的新字体。CDRF_NOTIFYSUBITEMDRAW 列表子项绘制时发送消息。CDRF_SKIPDEFAULT 系统不必再绘制该子项。}
正文完
 0
包子
版权声明:本站原创文章,由 包子 于2020-08-02发表,共计3191字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)