针对NM_CUSTOMDRAW消息的学习

消息的形式:1 窗口消息,2 命令消息,3 WM_NOTIFY消息,4 自定义消息

我们的NM_CUSTOMDRAW消息就是就属于第三种WM_NOTIFY消息,而添加消息映射的方法分为两种:

BEGIN_MESSAGE_MAP(CListCtrlColor, CListCtrl)

ON_NOTIFY_REFLECT(NM_CUSTOMDRAW,OnCustomMyList)

END_MESSAGE_MAP()

第一种用到的是消息反射机制:

在Windows的消息处理中,控制子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得控制子窗口的自身能动性大大降低(你想,它连改变自己 的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了反射消息“Reflect Message”的概念,进行消息反射,可以使得控制子窗口能够自行处理与自身相关的一些消息,增强了封装性,从而提高了控制子窗口的可重用性。通常在封装一些类是就用这个,比如本文中用的封装类,就是以CListCtrl控件作为基类。

BEGIN_MESSAGE_MAP(CListCtrlColor,CListCtrl)

ON_NOTIFY(NM_CUSTOMDRAW,IDC_LIST,OnCustomMyLIst)

END_MESSAGE_MAP()

第二种则常用在父窗口中不用反射,直接由父窗口处理该消息。例如:创建基于对话框的窗口,在其上加入CListCtrl控件,则用第二种对该控件发生的变化做出处理。

LRESULT参数:该参数相当于函数的返回值函数执行以后系统可以根据该结果作出相应的操作。

函数执行原理:该函数处理NM_CUSTOMDRAW消息的,只要触发该消息就会执行该函数,在本文中ListCtrl控件的重绘都会出发该消息。

函数执行过程:

该处理函数将控件的绘制分为两部分:擦除和绘画。Windows在每部分的开始和结束都会发送NM_CUSTOMDRAW消息。所以总共就有4个消息。但是 实际上你的程序所收到消息可能就只有1个或者多于四个,这取决于你想要让WINDOWS怎么做。每次发送消息的时段被称作为一个“绘画段”。你必须紧紧抓 住这个概念,因为它贯穿于整个“重绘”的过程。

所以,你将会在以下的时间点收到通知:

l 一个item被画之前——“绘画前”段
l 一个item被画之后——“绘画后”段
l 一个item被擦除之前——“擦除前”段
l 一个item被擦除之后——“擦除后”段

并不是所有的消息都是一样有用的,实际上,我不需要处理所有的消息,直到这篇文章完成之前,我还没使用过擦除前和擦除后的消息。所以,不要被这些消息吓到你。

NM_CUSTOMDRAW消息将会给你提供以下的信息:

1.lListCtrl的句柄

2.ListCtrl的ID

3.当前的“绘画段”

4.绘画的DC,让你可以用它来画画

5.正在被绘制的控件、item、subitem的RECT值

6.正在被绘制的Item的Index值

7.正在被绘制的SubItem的Index值

8.正被绘制的Item的状态值(selected,
grayed, 等等)

9.lItem的LPARAM值,就是你使用CListCtrl::SetItemData所设的那个值

其中4的获得方法:CDC
*pDC=CDC::FromHandle(pLVCD->nmcd.hdc);

其中5的获得方法:item=(int)pLVCD->nmcd.dwItemSpec;subitem=
pLVCD->iSubItem;

这两个图分别是刚才那两个值的对应值,图一中返回值解释如下

从图一中我们可以看到返回值分为两个部分:

1.当pLVCD->nmcd.dwDrawStage= CDDS_PREPAINT时即绘画周期开始前:

CDRF_DODEFAULT:返回该值之后该控件自己绘制,整个绘画周期内他不会发送任何其他的NM_CUSTOMDRAW消息,

CDRF_NOTIFYITEMDRAW:该返回值返回以后会通知父窗口相关项的绘画操作,并且在项的绘画开始前和开始后都会发送NM_CUSTOMDRAW;

2.当pLVCD->nmcd.dwDrawStage=CDDS_ITEMPREPAINT时即在该项绘画开始前

CDRF_NOTIFYSUBITEMDRAW:在视图项被绘制之前,你的应用程序会收到一个NM_CUSTOMDRAW消息,该消息中的dwDrawStage为CDDS_ITEMPREPAINT | CDDS_SUBITEM,这时你可以指定画笔和颜色对每个子项进行修改,否则返回CDRF_DODEFAULT默认处理。

CDRF_NEWFONT:你的应用程序使用指定的画笔,然后控件将会调用。

CDRF_SKIPDEFAULT:应用程序绘制这个项,控件不绘制。

上面是我们用到的其他自己研究吧!

看了上面的解释也许你还是一头雾水,那这里我就给你解答:

首先你要明白函数的过程是一个简单的绘画操作,绘画周期开始前 ,绘画前,绘画中,绘画后,你第一次触发NM_CUSTOMDRAW消息肯定是绘画开始前的所以第一个if语句肯定是成立的,这样就执行了第一个if语句,而接下来绘画前,绘画中,绘画后都不会触发,都不执行,这样一个关键点就是返回值了LRESULT了,其返回值就决定了在绘画过程中会不会触发NM_CUSTOMDRAW,这样你也就明白了第一个if中的返回值了吧。

而第二个if是绘画前和第一个的道理是一样的,从上面的解释中我们可以看到其返回值有三个,这三个中我尤其有说明的是CDRF_NOTIFYSUBITEMDRAW,因为该返回值决定是否触发消息进入绘画中,后面的代码中我们会看到在绘画中修改代码……….。

hdr NMHDR对象

dwDrawStage 当前绘制状态,其取值如表7所示:

类型值 含义

CDDS_POSTERASE 擦除循环结束

CDDS_POSTPAINT 绘制循环结束

CDDS_PREERASE 准备开始擦除循环

CDDS_PREPAINT 准备开始绘制循环

CDDS_ITEM 指定dwItemSpec, uItemState, lItemlParam参数有效

CDDS_ITEMPOSTERASE 列表项擦除结束

CDDS_ITEMPOSTPAINT 列表项绘制结束

CDDS_ITEMPREERASE 准备开始列表项擦除

CDDS_ITEMPREPAINT 准备开始列表项绘制

CDDS_SUBITEM 指定列表子项

表7 dwDrawStage的类型值与含义

hdc指定了绘制操作所使用的设备环境。

rc指定了将被绘制的矩形区域。

dwItemSpec 列表项的索引

uItemState 当前列表项的状态,其取值如表8所示:

类型值 含义

CDIS_CHECKED 标记状态。

CDIS_DEFAULT 默认状态。

CDIS_DISABLED 禁止状态。

CDIS_FOCUS 焦点状态。

CDIS_GRAYED 灰化状态。

CDIS_SELECTED 选中状态。

CDIS_HOTLIGHT 热点状态。

CDIS_INDETERMINATE 不定状态。

CDIS_MARKED 标注状态。

表8 uItemState的类型值与含义

lItemlParam 当前列表项的绑定数据

pResult 指向状态值的指针,指定系统后续操作,依赖于dwDrawStage:

当dwDrawStage为CDDS_PREPAINT,pResult含义如表9所示:

类型值 含义

CDRF_DODEFAULT 默认操作,即系统在列表项绘制循环过程不再发送NM_CUSTOMDRAW。

CDRF_NOTIFYITEMDRAW 指定列表项绘制前后发送消息。

CDRF_NOTIFYPOSTERASE 列表项擦除结束时发送消息。

CDRF_NOTIFYPOSTPAINT 列表项绘制结束时发送消息。

表9 pResult的类型值与含义(一)

当dwDrawStage为CDDS_ITEMPREPAINT,pResult含义如表10所示:

类型值 含义

CDRF_NEWFONT 指定后续操作采用应用中指定的新字体。

CDRF_NOTIFYSUBITEMDRAW 列表子项绘制时发送消息。

CDRF_SKIPDEFAULT 系统不必再绘制该子项。

时间: 2024-11-09 05:10:17

针对NM_CUSTOMDRAW消息的学习的相关文章

17 | 如何正确地显示随机消息? 学习记录

<MySQL实战45讲>17 | 如何正确地显示随机消息? 学习记录http://naotu.baidu.com/file/ee69cc1cd0aefb09e02efbaf9a3b2909?token=ba56dcf185ed4e27 原文地址:https://www.cnblogs.com/jtfr/p/11368240.html

iOS消息转发学习笔记

如果深入学习ios Runtime,不得不提到消息转发,很多框架的实现都基于这一功能实现(例如JSPatch) 虽然看了很多篇关于消息转发的文章,但是理解的不是很透彻,还是自己实践一些理解能更加透彻一下. 首先我自己定义了一个MyString继承NSString @interface MyString : NSString @end @implementation MyString @end 然后创建一个MyString,通过performSelector调用MissMethod,MissMet

消息队列学习一 概念

消息中间件随着分布式系统的发展变得越来越火,下面就学习课程过程对消息中间件进行解释 概念:(部分内容引用自:享学课堂课件) 其实并没有标准定义.一般认为,消息中间件属于分布式系统中一个子系统,关注于数据的发送和接收,利用高效可靠的异步消息传递机制对分布 式系统中的其余各个子系统进行集成. 高效:对于消息的处理处理速度快. 可靠:一般消息中间件都会有消息持久化机制和其他的机制确保消息不丢失. 异步:指发送完一个请求,不需要等待返回,随时可以再发送下一个请求,既不需要等待. 一句话总结,我们消息中间

NM_CUSTOMDRAW 消息

When the control first starts to paint itself, in response to a WM_PAINT, you receive a NM_CUSTOMDRAW notification message, with the draw stage set to CDDS_PREPAINT. If you don’t handle this yourself that will be the end of it, as the default message

rabbitmq消息队列学习——&quot;工作队列&quot;

二."工作队列" 在第一节中我们发送接收消息直接从队列中进行.这节中我们会创建一个工作队列来分发处理多个工作者中的耗时性任务. 工作队列主要是为了避免进行一些必须同步等待的资源密集型的任务.实际上我们将这些任务时序话稍后分发完成.我们将某个任务封装成消息然后发送至队列,后台运行的工作进程将这些消息取出然后执行这些任务.当你运行多个工作进程的时候,这些任务也会在它们之间共享. 前期准备 上一节的练习中我们发送的是简单包含"Hello World!"的消息,这节我们还发

MQ(消息队列)学习

转自: http://book.51cto.com/art/201502/466288.htm         为什么我们需要MQ? 而这就是MQ :一个高效的可嵌入库,它解决了大部分应用程序需要解决的问题,变得在网络上有良好的可伸缩性,而没有多少成本. 具体做法是: 它在后台线程异步处理I/O.这些线程使用无锁数据结构与应用程序线程进行通信,所以并发MQ 应用程序不再需要锁.信号量,或其他等待状态. 组件可以动态地来去自如,而MQ 会自动重新连接.这意味着你可以以任何顺序启动组件.你可以创建“

Android消息机制学习笔记

Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑: MessageQueue:消息队列,它的内存存储了一组消息,以队列的形式对外提供插入和删除的工作,内部结构:采用 单链表的数据结构来存储消息队列: Looper:处理MessageQueue的消息,会以无限循环的形式去查找是否有新消息,有就处理无则等待,还要用到一个特殊的概念-ThreadLocal:并不是线程,作用:可以在每个线程中存储数据, ThreadLo

强化一下概念:程序自己不去取消息,消息不会自己跑过来运行(针对线程消息队列里的消息,也是绝大多数消息)

刚才看这段代码的时候: procedure TControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); begin if CheckNewSize(AWidth, AHeight) and ((ALeft <> FLeft) or (ATop <> FTop) or (AWidth <> FWidth) or (AHeight <> FHeight)) then begin InvalidateCon

网易视频云技术分享:Android 消息机制学习

Android消息机制大家都不陌生,想必大家也都看过Handler.Looper的源码(看过可以直接看末尾重点,一款监控APP卡顿情况的控件),下面,网易视频云技术专家就整合一下这方面的资料,加深对这方面的印象. 用法 private Handler mHandler = new Handler() {    @Override public void handleMessage(Message msg) {        switch (msg.what) {            case