- 浏览: 71197 次
- 性别:
- 来自: 上海
文章分类
最新评论
common control 4.7版本介绍了一个新的特性叫做Custom Draw,这个名字显得模糊不清,让人有点摸不着头脑,而且MSDN里也只给出了一些如风的解释和例子,没有谁告诉你你想知道的,和究竟这个特性有什么好处。
Custom draw可以被想象成一个轻量级的,容易使用的重绘方法(重绘方法还有几种,例如Owner Draw等)。这种容易来自于我们只需要处理一个消息(NM_CUSTOMDRAW),就可以让Windows为你干活了,你就不用被逼去处理"重绘过程"中所有的脏活了。
这篇文章的焦点是如何在一个LISTCTRL控件上使用Custom Draw消息。究其原因,一部分是因为我已经在我的工作上使用了Custom Draw有一段时间了,我很熟悉它。另一个原因是这个机制确实是非常好用,你只需要写很少量的代码就可以达到很好的效果。使用 Custom draw 来对控件外观编程甚至可以代替很多的古老方法。
以下代码是在WIN98 和VC6 SP2的环境下写的,common controls DLL的版本是5.0。我已经对其在WinNT 4上进行了测试。系统要运行这些代码,它的common controls DLL的版本必须至少是4.71。但随着IE4 的发布,这已经不是问题了。(IE会夹带着这个DLL一起发布)
Custom Draw 基础
我将会尽我所能把Custom Draw的处理描述清楚,而不是简单的引用MSDN的文档。这些例子都需要你的程序有一个ListCtrl在对话框上,并且这个ListCtrl处于Report和多列模式。
Custom Draw 的消息映射入口
Custom draw 是一个类似于回调的处理过程,Windows在绘制List Ctrl的某个时间点上通过 Notification 消息通知你的程序,你可以选择忽略所有的通知(这样你就会看到标准的ListCtrl),或者处理某部分的绘制(实现简单的效果),甚至整个的控件都由你来绘制(就象使用Owner-Drawing一样)。这个机制的真正卖点是:你只需要实现一些你需要的,其余的可以让Windows为你代劳。
好了,现在你可以开始为你的ListCtrl添加Custom Draw去做一些个性化的事情了。你首先要有正确的Comm Ctrl Dll版本,然后Windows会为你发送NM_CUSTOMDRAW消息,你只需要添加一个处理函数以便开始使用Custom draw。首先添加一个消息映射,象下面一样:
ON_NOTIFY ( NM_CUSTOMDRAW, IDC_MY_LIST, OnCustomdrawMyList )处理函数的原形如下:
afx_msg void OnCustomdrawMyList ( NMHDR* pNMHDR, LRESULT* pResult );这就告诉MFC你要处理从你的ListCtrl控件发出的WM_NOTIFY消息,ID为IDC_MY_LIST,通知码为NM_CUSTOMDRAW,OnCustomdrawMyList就是你的处理函数。
如果你有一个从ClistCtr派生的类,你想为它添加custom draw,你就可以使用ON_NOTIFY_REFLECT来代替。如下:
ON_NOTIFY_REFLECT ( NM_CUSTOMDRAW, OnCustomdraw )OnCustomdraw的原形和上面的函数一致,但它是声明在你的派生类里的。
Custom draw将控件的绘制分为两部分:擦除和绘画。Windows在每部分的开始和结束都会发送NM_CUSTOMDRAW消息。所以总共就有4个消息。但是实际上你的程序所收到消息可能就只有1个或者多于四个,这取决于你想要让WINDOWS怎么做。每次发送消息的时段被称作为一个“绘画段”。你必须紧紧抓住这个概念,因为它贯穿于整个“重绘”的过程。
所以,你将会在以下的时间点收到通知:
l 一个item被画之前——“绘画前”段
l 一个item被画之后——“绘画后”段
l 一个item被擦除之前——“擦除前”段
l 一个item被擦除之后——“擦除后”段
并不是所有的消息都是一样有用的,实际上,我不需要处理所有的消息,直到这篇文章完成之前,我还没使用过擦除前和擦除后的消息。所以,不要被这些消息吓到你。
NM_CUSTOMDRAW Messages提供给你的信息:
l NM_CUSTOMDRAW消息将会给你提供以下的信息:
l ListCtrl的句柄
l ListCtrl的ID
l 当前的“绘画段”
l 绘画的DC,让你可以用它来画画
l 正在被绘制的控件、item、subitem的RECT值
l 正在被绘制的Item的Index值
l 正在被绘制的SubItem的Index值
l 正被绘制的Item的状态值(selected, grayed, 等等)
l Item的LPARAM值,就是你使用CListCtrl::SetItemData所设的那个值
上述所有的信息对你来说可能都很重要,这取决于你想实现什么效果,但最经常用到的就是“绘画段”、“绘画DC”、“Item Index”、“LPARAM”这几个值。
一个简单的例子:
好了,经过上面的无聊的细节之后,我们是时候来看一些简单的代码了。第一个例子非常的简单,它只是改变了一下控件中文字的颜色。
处理的代码如下:
void CPanel1::OnCustomdrawList ( NMHDR* pNMHDR, LRESULT* pResult ){ NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
// Take the default processing unless we set this to something else below. *pResult = 0;
// First thing - check the draw stage. If it's the control's prepaint // stage, then tell Windows we want messages for every item. if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage ) { *pResult = CDRF_NOTIFYITEMDRAW; } else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage ) { // This is the prepaint stage for an item. Here's where we set the // item's text color. Our return value will tell Windows to draw the // item itself, but it will use the new color we set here. // We'll cycle the colors through red, green, and light blue. COLORREF crText;
if ( (pLVCD->nmcd.dwItemSpec % 3) == 0 ) crText = RGB(255,0,0); else if ( (pLVCD->nmcd.dwItemSpec % 3) == 1 ) crText = RGB(0,255,0); else crText = RGB(128,128,255);
// Store the color back in the NMLVCUSTOMDRAW struct. pLVCD->clrText = crText;
// Tell Windows to paint the control itself. *pResult = CDRF_DODEFAULT; }}
结果如下,你可以看到行和行间的颜色的交错显示,多酷,而这只需要两个if的判断就可以做到了。
有一件事情必须记住,在做任何的绘画之前,你都要检查正处身的“绘画段”,因为你的处理函数会接收到非常多的消息,而“绘画段”将决定你代码的行为。
一个更小的简单例子:
下面的例子将演示怎么去处理subitem的绘画(其实subitem也就是列)
在ListCtrl控件绘画前处理NM_CUSTOMDRAW消息。
告诉Windows我们想对每个Item处理NM_CUSTOMDRAW消息。
当这些消息中的一个到来,告诉Windows我们想在每个SubItem的绘制前处理这个消息
当这些消息到达,我们就为每个SubItem设置文字和背景的颜色。
void CMyDlg::OnCustomdrawMyList ( NMHDR* pNMHDR, LRESULT* pResult )
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
// Take the default processing unless we set this to something else below.
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
elseif ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
{
// This is the notification message for an item. We'll request
// notifications before each subitem's prepaint stage.
*pResult = CDRF_NOTIFYSUBITEMDRAW;
}
elseif ( (CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage )
{
// This is the prepaint stage for a subitem. Here's where we set the
// item's text and background colors. Our return value will tell
// Windows to draw the subitem itself, but it will use the new colors
// we set here.
// The text color will cycle through red, green, and light blue.
// The background color will be light blue for column 0, red for
// column 1, and black for column 2.
COLORREF crText, crBkgnd;
if ( 0 == pLVCD->iSubItem )
{
crText = RGB(255,0,0);
crBkgnd = RGB(128,128,255);
}
elseif ( 1 == pLVCD->iSubItem )
{
crText = RGB(0,255,0);
crBkgnd = RGB(255,0,0);
}
else
{
crText = RGB(128,128,255);
crBkgnd = RGB(0,0,0);
}
// Store the colors back in the NMLVCUSTOMDRAW struct.
pLVCD->clrText = crText;
pLVCD->clrTextBk = crBkgnd;
// Tell Windows to paint the control itself.
*pResult = CDRF_DODEFAULT;
}
}
执行的结果如下:
这里需要注意两件事:
l clrTextBk的颜色只是针对每一列,在最后一列的右边那个区域颜色也还是和ListCtrl控件的背景颜色一致。
l 当我重新看文档的时候,我注意到有一篇题目是“NM_CUSTOMDRAW (list view)”的文章,它说你可以在最开始的custom draw消息中返回CDRF_NOTIFYSUBITEMDRAW就可以处理SubItem了,而不需要在CDDS_ITEMPREPAINT绘画段中去指定CDRF_NOTIFYSUBITEMDRAW。但是我试了一下,发现这种方法并不起作用,你还是需要处理CDDS_ITEMPREPAINT段。
处理“绘画之后”的段
到限制为止的例子都是处理“绘画前”的段,当Windows绘制List Item之前就改变它的外观。然而,在“绘制前”,你的绘制行为时被限制的,你只能改变字体的颜色或者外观。如果你想改变图标的绘制,你可以在“绘画前”把整个 Item重画或者在“绘画后”去做这件事。当你做在绘画后去做“自定义绘画”是,你的“绘画处理函数”就会在Windows画完整个Item或者SubItem的时候被调用,你就可以随心所欲的乱画了!!
在这个例子里,我将创建一个ListCtrl,一般的ListCtrl的Item如果被选择了,则其Icon也会呈现出被选择的状态。而我创建的这个ListCtrl的Icon是不会呈现被选择的状态的。步骤如下:
对ListCtrl在“绘画前”处理NM_CUSTOMDRAW消息。
告诉Windows我们想在每个Item被画的时候获得NM_CUSTOMDRAW消息。
当这些消息来临,告诉Windows我们想在你画完的时候获取NM_CUSTOMDRAW消息。
当这些消息来到的时候,我们就重新画每一个Item的图标。
void CPanel3::OnCustomdrawList ( NMHDR* pNMHDR, LRESULT* pResult ){ NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR ); *pResult = 0; // If this is the beginning of the control's paint cycle, request // notifications for each item. if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage ) { *pResult = CDRF_NOTIFYITEMDRAW; } else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage ) { // This is the pre-paint stage for an item. We need to make another // request to be notified during the post-paint stage. *pResult = CDRF_NOTIFYPOSTPAINT; } else if ( CDDS_ITEMPOSTPAINT == pLVCD->nmcd.dwDrawStage ) { // If this item is selected, re-draw the icon in its normal // color (not blended with the highlight color). LVITEM rItem; int nItem = static_cast<int>( pLVCD->nmcd.dwItemSpec ); // Get the image index and state of this item. Note that we need to // check the selected state manually. The docs _say_ that the // item's state is in pLVCD->nmcd.uItemState, but during my testing // it was always equal to 0x0201, which doesn't make sense, since // the max CDIS_ constant in commctrl.h is 0x0100. ZeroMemory ( &rItem, sizeof(LVITEM) ); rItem.mask = LVIF_IMAGE | LVIF_STATE; rItem.iItem = nItem; rItem.stateMask = LVIS_SELECTED; m_list.GetItem ( &rItem ); // If this item is selected, redraw the icon with its normal colors. if ( rItem.state & LVIS_SELECTED ) { CDC* pDC = CDC::FromHandle ( pLVCD->nmcd.hdc ); CRect rcIcon; // Get the rect that holds the item's icon. m_list.GetItemRect ( nItem, &rcIcon, LVIR_ICON ); // Draw the icon. m_imglist.Draw ( pDC, rItem.iImage, rcIcon.TopLeft(), ILD_TRANSPARENT ); *pResult = CDRF_SKIPDEFAULT; } }}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/love19840109/archive/2009/11/01/4755014.aspx
重复,custom draw让我们可以做尽可能少的工作,上面的例子就是让Windows帮我们做完全部的工作,然后我们就重新对选择状态的Item的图标做重画,那就是我们看到的那个图标。执行结果如下:唯一的不足是,这样的方法会让你感觉到一点闪烁。因为图标被画了两次(虽然很快)。 用Custom Draw代替Owner Draw另外一件优雅的事情就是你可以使用Custom Draw来代替Owner Draw。它们之间的不同在我看来就是:
l 写Custom Draw的代码比写Owner Draw的代码更容易。
如果你只需要改变某行的外观,你可以不用管其他的行的绘画,让WINDOWS去做就行了。但如果你使用
Owner Draw,你必须要对所有的行作处理。当你想对控件作所有的处理时,你可以在处理NM_CUSTOMDRAW
消息的最后返回CDRF_SKIPDEFAULT,这有点和我们到目前为止所做的有些不同。CDRF_SKIPDEFAULT
告诉Windows由我们来做所有的控件绘画,你不用管任何事。
我没有在这里包含这个例子的代码,因为它有点长,但是你可以一步步地在调试器中调试代码,你可以看到每一
步发生了什么。如果你把窗口摆放好,让你可以看到调试器和演示的程序,那在你一步步的调试中,你可以看到
控件每一步的绘制,这里的ListCtrl是很简单的,只有一列并且没有列头,如下:
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/love19840109/archive/2009/11/01/4755014.aspx
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/love19840109/archive/2009/11/01/4755014.aspx
发表评论
-
自己写的一段customerDraw
2010-08-24 16:20 1856void CNewReportPage::OnNMCustom ... -
帖BitMap
2010-08-24 09:42 746void CEx_14View::O ... -
clistctrl +号按钮绘制
2010-08-11 17:18 1521网上有关clistctrl举不胜举,包括按钮控件的绘 ... -
listctrl总结2
2010-08-11 17:11 94520. listctrl的subitem添加图标 ... -
VC中使用ListCtrl经验总结(1)
2010-08-11 17:07 1279ListCtrl在工作中,常常用到,也常常看到大家发帖问怎么用 ... -
LV_COLUMN结构体
2010-08-10 14:44 2637typedef struct _LV_COLUMN {UINT ... -
加载位图BITMAP
2010-08-10 14:27 1401m_Bitmap.LoadBitmap(IDB_BITMAP1 ... -
关于控件重绘函数/消息 OnPaint,OnDraw,OnDrawItem,DrawItem的区别
2010-08-10 13:15 6377而OnPaint()是CWnd的类成员,同时负责响应WM_ ... -
OnCreate PreCreateWindow PreSubclassWindow
2010-08-09 17:47 1723OnCreate PreCreateWindow PreSub ... -
扩展CListCtrl实现颜色长度改变
2010-08-09 17:34 3163用CListCtrl来显示数据比较方便,有时候我们需要标注某一 ... -
创建CListCtrl中的排序小图标
2010-08-09 17:11 2272创建CListCtrl中的排序小图标 for(int i = ... -
CListCtrl控件的使用
2010-08-09 17:02 1155初始化: DWORD dwStyle; dwStyle = m ... -
改变 CListCtrl、CHeaderCtrl 高度、字体、颜色和背景
2010-08-09 16:49 2566改变 CListCtrl、CHeaderCtrl 高度、字体、 ... -
CHeaderCtrl用法
2010-08-09 15:47 4202CListCtrl的表头可以单独 ... -
CListCtrl::SortItems的用法:
2010-08-09 12:37 1954CListCtrl::SortItems的用法: (一)So ... -
树型视的三个结构TVINSERTSTRUCT、TVITEM、NMTREEVIEW
2010-08-05 18:11 953TVINSERTSTRUCT包含添加新项到树形视控件所使用的信 ... -
剪贴板和OLE拖放
2010-08-05 16:35 1084一、传统剪贴板 ... -
文档/视图结构中的各个部分是如何联系到一起的
2010-08-05 15:39 715文档/视图结构是MFC中最有特色而又有难度的部分,在这当中涉及 ... -
MFC一些函数区别
2010-08-05 13:43 1610GetMessagePos GetCursorPos的区别 ... -
LPTSTR、LPCSTR、LPCTSTR、LPSTR的意义
2010-08-05 13:33 986UNICODE:它是用两个字节 ...
相关推荐
ListCtrl重绘,增加combox选择项
listctrl重绘,包括表头,表内容,行高,字体颜色和尺寸。
列表重绘类 某高人写的 实现了列表中编辑框 复选框 单选按钮 下拉列表框 百分比进度条 设置颜色 禁用某项 编辑某项(限文本框) 此类可根据自己需要扩展 一些功能 比如改列表背景色之类的。。
这是一个重绘的 MFC List control 控件 ,里面包含了重绘的progress control控件、combo box控件 以及一个button 控件,可以在list control 控件的单元格中插入这些控件,便于更好的展示信息,对于初学者学习重绘...
介绍了ListCtrl控件的自绘功能,实现ListCtrl颜色的设置。
重绘带checkbox的ClistCtrl,包括表头,表项,滚动条全部重绘。
重绘MFC列表控件(ListCtrl)字体大小 表头和行高度 行颜色等 行的颜色有5中:选中(获取焦点和丢失焦点2种),未选中(鼠标移动时、奇行和偶行)等
收集的一些列表框的重绘例子,值得参考,适合重绘学习!
自绘列表控件 可任意更换滚动条图片
MFC ListCtrl 重绘,重绘列表的颜色
使用Custom Draw优雅的实现ListCtrl的重绘,可改变字体的颜色,大小,背景颜色,加载图片
ColoredListCtrl 重绘LIstCtrl 字体颜色
vs2005自己写的一个工具上的 两个个字体替换窗口() 只是界面和一些响应事件主要的东西就是一个列表 用了list重绘类 此工程中的重绘类被我改过 直接复用可能会报错 未改过的可在我资源(listctrl重绘类)中下载
实现了自绘ListCtrl控件,我相信有大家需要的资源
可编辑ListCtrl控件实例
该项目需要使用VS2008来打开,项目主要是对一些基本常用的控件进行重绘,包含有ListCtrl控件、Edit控件、Combox控件...一些基本的控件
MFC实现ListControl控件的自绘,选中行颜色变化,行间距设置,行字体大小设置等
MFC重绘CListCtrl的scrollbar,headerctrl和items的代码.用于MFC的UI的设计。
树形listctrl,双击可编辑,右键添加删除,重绘表头,设置表头字体
MFC ListCtrl重绘,实现高亮选中单元格