大家也许熟悉WM_NOTIFY,控件通过WM_NOTIFY向父窗口发送消息。在WM_NOTIFY消息体中,部分控件会发送NM_CUSTOMDRAW告诉父窗口自己需要绘图。
也可以反射NM_CUSTOMDRAW消息,如:
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) //需要自己加进去
afx_msg void OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult);
参数:
pNMHDR 说到底只是一个指针,大多数情况下它指向一个NMHDR结构对象,NMHDR结构如下:
typedef struct tagNMHDR
{
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
其中:
hwndFrom 发送方控件的窗口句柄
idFrom 发送方控件的ID
code 通知代码
对于某些控件来说,pNMHDR则会解释成其它内容更丰富的结构对象的指针,如:对于列表控件来说,pNMHDR常常指向一个NMCUSTOMDRAW对象,NMCUSTOMDRAW结构如下:
typedef struct tagNMCUSTOMDRAWINFO
{
NMHDR hdr;
DWORD dwDrawStage;
HDC hdc;
RECT rc;
DWORD dwItemSpec;
UINT uItemState;
LPARAM lItemlParam;
} NMCUSTOMDRAW, FAR * LPNMCUSTOMDRAW;
其中:
hdr NMHDR对象
dwDrawStage 当前绘制状态,其取值如下:
CDDS_POSTERASE | 擦除循环结束 |
CDDS_POSTPAINT | 绘制循环结束 |
CDDS_PREERASE | 准备开始擦除循环 |
CDDS_PREPAINT | 准备开始绘制循环 |
CDDS_ITEM | 指定dwItemSpec, uItemState, lItemlParam参数有效 |
CDDS_ITEMPOSTERASE | 列表项擦除结束 |
CDDS_ITEMPOSTPAINT | 列表项绘制结束 |
CDDS_ITEMPREERASE | 准备开始列表项擦除 |
CDDS_ITEMPREPAINT | 准备开始列表项绘制 |
CDDS_SUBITEM | 指定列表子项 |
hdc指定了绘制操作所使用的设备环境。
rc指定了将被绘制的矩形区域。
dwItemSpec 列表项的索引
uItemState 当前列表项的状态,其取值如下:
CDIS_CHECKED | 标记状态 |
CDIS_DEFAULT | 默认状态 |
CDIS_DISABLED | 禁止状态 |
CDIS_FOCUS | 焦点状态 |
CDIS_GRAYED | 灰化状态 |
CDIS_SELECTED | 选中状态 |
CDIS_HOTLIGHT | 热点状态 |
CDIS_INDETERMINATE | 不定状态 |
CDIS_MARKED标注状态 |
lItemlParam 当前列表项的绑定数据
pResult 指向状态值的指针,指定系统后续操作,依赖于dwDrawStage:
当dwDrawStage为CDDS_PREPAINT,pResult含义如下:
CDRF_DODEFAULT | 默认操作,即系统在列表项绘制循环过程不再发送NM_CUSTOMDRAW |
CDRF_NOTIFYITEMDRAW | 指定列表项绘制前后发送消息 |
CDRF_NOTIFYPOSTERASE | 列表项擦除结束时发送消息 |
CDRF_NOTIFYPOSTPAINT | 列表项绘制结束时发送消息 |
当dwDrawStage为CDDS_ITEMPREPAINT,pResult含义如下:
CDRF_NEWFONT | 指定后续操作采用应用中指定的新字体 |
CDRF_NOTIFYSUBITEMDRAW | 列表子项绘制时发送消息 |
CDRF_SKIPDEFAULT | 系统不必再绘制该子项 |
以下是一个利用NM_CUSTOMDRAW消息绘制出的多色列表框的例子:
对应代码如下:
void CCoolList::OnCustomDraw //从CListCtrl派生(NMHDR *pNMHDR, LRESULT *pResult)
{
//类型安全转换
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
*pResult = 0; //指定列表项绘制前后发送消息
if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if(CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage)
{
//奇数行
if(pLVCD->nmcd.dwItemSpec % 2)
pLVCD->clrTextBk = RGB(255, 255, 128);
//偶数行
else
pLVCD->clrTextBk = RGB(128, 255, 255);
//继续
*pResult = CDRF_DODEFAULT;
}
}
- 注意到上例采取了3.1所推荐的第2种实现方法,派生了一个新类CCoolList。
总体步骤:
- 派生CCoolList类
- BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CMyListCtrl)
// NOTE - the ClassWizard will add and remove mapping macros here.
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) //自己添加
//}}AFX_MSG_MAP
END_MESSAGE_MAP() - 定义CListCtrl Control m_list变量,再将CListCtrl 改为CCoolList