VC控件自绘制三步曲
http://blog.csdn.net/lijie45655/article/details/6362441
实现自定义绘制的三步曲
既然您已经了解了绘制控件可用的各种选项(包括使用自定义绘制的好处),那么,让我们来看看实现一个自定义绘制控件需要的三个主要步骤。
执行一个 NM_CUSTOMDRAW 消息处理程序。
指定处理所需的绘制阶段。
筛选特定的绘制阶段(在这些阶段中,您需要加入自己的特定于控件的绘制代码)。
执行一个NM_CUSTOMDRAW 消息处理程序
当需要绘制一个公共控件时,MFC 会将控件的自定义绘制通知消息(最初发送到控件的父窗口)以NM_CUSTOMDRAW 消息的形式反馈给控件。以下是一个 NM_CUSTOMDRAW 处理程序的示例。
void CMyCustomDrawControl::OnCustomDraw(NMHDR* pNMHDR,
LRESULT* pResult)
{
LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);
...
}
正如您所见,NM_CUSTOMDRAW 处理程序将一个指针传递给 NMHDR 类型的结构。然而,该值不足以用于象NMHDR 这样只包含三个成员(hwndFrom、idFrom 和 code)的结构。
因此,您通常需要将该结构指针转换为信息量更大的结构 — LPNMCUSTOMDRAW。LPNMCUSTOMDRAW 指向 NMCUSTOMDRAW,它包含诸如 dwDrawStage、dwItemSpec 和 uItemState 这样的成员 — 它们是决定当前绘制阶段及确切绘制(例如,控件本身、或控件的一个项目或子项)所必需的。
这里值得注意的是,还可以将 NMHDR 指针指向特定于正在绘制控件的类型的结构。表 1 显示控件的一个列表及其相关的自定义绘制结构类型名。
表 1:控件及其相关的自定义绘制结构
控件 |
结构(在 commctrl.h 中定义) |
---|---|
Rebar、Trackbar、AuthTicket、My.Resources、My.Settings、My.User 和 My.WebServices。 |
NMCUSTOMDRAW |
List-view |
NMLVCUSTOMDRAW |
Toolbar |
NMTBCUSTOMDRAW |
Tooltip |
NMTTCUSTOMDRAW |
Tree-view |
NMTVCUSTOMDRAW |
指定处理所需的绘制阶段
正如我在前面提到的,绘制一个控件存在一些“阶段”。特别是,您可以将绘制过程理解为一系列阶段,其中控件通知其父窗口需要绘制的内容。事实上,控件甚至会在绘制控件及其各项前后发送一个通知,从而让编程人员更好地控制该过程。
在所有情况下,单一的 NM_CUSTOMDRAW 处理程序在每个绘制阶段都进行调用。然而,谨记:自定义绘制允许您在自己的绘制中合并默认的控件绘制,您需要指定您将处理哪个绘制阶段。这通过设置NM_CUSTOMDRAW 处理程序的第二个参数 (pResult) 完成。事实上,如果您从未设置该值,则用初始阶段的CDDS_PREPAINT 调用函数后,您的函数将不再被调用!
从技术上讲,只有两个阶段指定需要的绘制阶段(CDDS_PREPAINT 和 CDDS_ITEMPREPAINT),它们影响发送通知消息的内容。然而,通常只在处理程序的最后指定代码将处理的绘制阶段。表 2 列出用于指定所需绘制阶段(代码关注的)的值。
表 2:自定义绘制返回标志
自定义绘制返回标志 |
含义 |
---|---|
CDRF_DEFAULT |
指示控件自行绘制。该值为默认值,不应该将它与其他值组合在一起。 |
CDRF_SKIPDEFAULT |
用于指定控件根本不进行任何绘制。 |
CDRF_NEWFONT |
当代码更改绘制项/子项的字体时使用。 |
CDRF_NOTIFYPOSTPAINT |
使通知信息在控件或每个项/子项绘制后发送。 |
CDRF_NOTIFYITEMDRAW |
指出项(或子项)将进行绘制。注意,它下面的值与 CDRF_NOTIFYSUBITEMDRAW 相同。 |
CDRF_NOTIFYSUBITEMDRAW |
指出子项(或项)将进行绘制。注意,它下面的值与 CDRF_NOTIFYITEMDRAW 相同。 |
CDRF_NOTIFYPOSTERASE |
当删除控件后需要通知代码时使用。 |
以下为一个示例,其中的代码指定,当绘制控件的项 (CDRF_NOTIFYITEMDRAW) 及子项 (CDRF_NOTIFYPOSTPAINT),以及绘制完成时,应该调用 NM_CUSTOMDRAW 处理程序。
void CListCtrlWithCustomDraw::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); ... *pResult = 0; // Initialize value
*pResult |= CDRF_NOTIFYITEMDRAW;
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
*pResult |= CDRF_NOTIFYPOSTPAINT;
}
筛选指定的绘制阶段
一旦指定要关注的阶段后,您需要处理这些阶段。因为绘制过程的每个阶段只有一个消息要发送,惯例是执行一个 switch 语句以决定准确的绘制阶段。不同的绘制阶段由以下标志定义:
CDDS_PREPAINT
CDDS_ITEM
CDDS_ITEMPREPAINT
CDDS_ITEMPOSTPAINT
CDDS_ITEMPREERASE
CDDS_ITEMPOSTERASE
CDDS_SUBITEM
CDDS_POSTPAINT
CDDS_PREERASE
CDDS_POSTERASE
对于一个 CListCtrl 派生的类,有一个 NM_CUSTOMDRAW 处理程序的示例,其中您可以发现,代码决定当前绘制阶段的方式:
void CMyCustomDrawControl::OnCustomDraw(NMHDR* pNMHDR,
LRESULT* pResult)
{
LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);
switch(pNMCD->dwDrawStage)
{
case CDDS_PREPAINT:
...
break; case CDDS_ITEMPREPAINT:
...
break; case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
...
break; ...
} *pResult = 0;
}
注意,为了决定子项(例如,列表视图控件)绘制的阶段,您必需使用按位 or 操作符,它有两个值:其中一个为 CDDS_ITEMPREPAINT 或者 CDDS_ITEMPOSTPAINT,另一个为 CDDS_SUBITEM。
要说明它,我们假定您想在绘制列表视图项之前进行一些处理。将编写 switch 语句来处理CDDS_ITEMPREPAINT。
case CDDS_ITEMPREPAINT:
...
break;
然而,如果是您所关注子项的预绘制阶段,则将如下操作:
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
...
break;
示例:创建一个列表视图控件自定义绘制控件
如前面提到的,您可以完全控制控件及其项的绘制,或者仅执行一小部分特定于应用程序的绘制,并让控件继续进行。本文的焦点更多地偏重于控件绘制技术而非高级的绘制技术,我们将演练一个简单的示例,其中列表视图控件是一个自定义的绘制,因此项的文本将在创建拼接外观的交替单元中显示为不同的颜色。
创建一个基于 Visual C++ 2005 对话框的项目,名为 ListCtrlColor。
从 Class View 中选择 Project 菜单选项,并单击 Add Class 调用 Add Class 对话框。
从分类列表中选择 MFC,然后从模板列表中选择 MFC Class。
单击 Add 按钮,调用 MFC Class Wizard 对话框。
对于 Class name,键入值 CListCtrlWithCustomDraw 并选择 CListCtrl 的 Base class。
单击 Finish 按钮,生成类的标头和执行文件。
对于 Class View,右键单击 CListCtrlWithCustomDraw 类,并选择 Properties 上下文菜单选项。
显示 Properties 窗口时,单击顶部的 Messages 按钮,显示一个两列的消息列表,您可以为其实现处理程序。
在消息列表中单击 NM_CUSTOMDRAW 项,然后下拉第二列的组合框箭头,并选择值OnNMCustomdraw。
现在,处理绘制代码。这里,我们只简单处理项和子项预绘制阶段,指定基于当前行(项)和列(子项)的文本和背景色。要进行此操作,按如下所示修改 OnNMCustomdraw 函数:
void CListCtrlWithCustomDraw::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast(pNMHDR); switch(lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_ITEMPREPAINT:
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
if (0 == ((lpLVCustomDraw->nmcd.dwItemSpec + lpLVCustomDraw->iSubItem) % 2))
{
lpLVCustomDraw->clrText = RGB(255,255,255); // white text
lpLVCustomDraw->clrTextBk = RGB(0,0,0); // black background
}
else
{
lpLVCustomDraw->clrText = CLR_DEFAULT;
lpLVCustomDraw->clrTextBk = CLR_DEFAULT;
}
break; default: break;
} *pResult = 0;
*pResult |= CDRF_NOTIFYPOSTPAINT;
*pResult |= CDRF_NOTIFYITEMDRAW;
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
}
现在,我们来测试新控件。要进行此操作,您只需使用 CListCtrlWithCustomDraw 类将列表视图控件放在对话框中,并对其进行子类派生。下面是完成该操作的步骤。
在 Resource 视图中,打开应用程序的主对话框 (IDD_LISTCTRLCOLOR_DIALOG)。
从 Toolbox 中,将一个 List Control 拖放到该对话框。
右键单击列表控件,并选择 Properties 上下文菜单选项。
将 View 属性设置为 Report。
右键单击控件,并选择 Add Variable 上下文菜单选项。
出现 Add Member Variable Wizard 对话框时,指定 m_lstBooks 的 Variable name,并单击 Finish按钮。
这时,您就有了一个 CListCtrl 派生类 (m_lstBooks),它将对话框上的列表视图控件进行子类派生。然而,m_lstBooks 需要从最新创建的 CListCtrlWithCustomDraw 派生,以便于调用您的绘制代码。因此,打开对话框的标题文件 (ListCtrlColorDlg.h),将 m_lstBooks 更改为 CListCtrlWithCustomDraw 类型。
在 CListCtrlColorDlg 类开始之前,添加以下指令。
#include "ListCtrlWithCustomDraw.h"
将下面的代码添加到对话框的 OnInitDialog 成员函数,这样我们就能够看到一些列表视图行。
// Insert the columns
m_lstBooks.InsertColumn(0, _T("Author"));
m_lstBooks.InsertColumn(1, _T("Book")); // Define the data
static struct
{
TCHAR m_szAuthor[50];
TCHAR m_szTitle[100];
} BOOK_INFO[] = {
_T("Tom Archer"), _T("Visual C++.NET Bible"),
_T("Tom Archer"), _T("Extending MFC with the .NET Framework"),
_T("Brian Johnson"), _T("XBox 360 For Dummies")
}; // Insert the data
int idx;
for (int i = 0; i < sizeof BOOK_INFO / sizeof BOOK_INFO[0]; i++)
{
idx = m_lstBooks.InsertItem(i, BOOK_INFO[i].m_szAuthor);
m_lstBooks.SetItemText(i, 1, BOOK_INFO[i].m_szTitle);
}现在,建立并运行应用程序。图 1 为应用程序外观的一个示例。
图 1. 自定义绘制示例应用程序
小结
当 Windows 首次作为“下一代”操作系统引入到应用程序开发之中时,它作为新图形用户界面的一个主要论据就是其一致性。该论据的要点所在是其具有一个通用的外观:统一的菜单项、通用控件等。这一通用性的感觉可能会一直延续,直到有第二家公司想设计其自己的应用程序。简单说,提供外观与其他应用程序雷同的应用程序,任何公司都不会逃离这一怪圈。
要建立一个唯一的且让人过目难忘的用户界面,其中一种方式是为应用程序设计并开发自定义的控件。希望本文能对您有所帮助,现在,您了解到一种非常强大的技术,它使您的应用程序能从众多竞争对手的应用程序中脱颖而出。
http://blog.csdn.net/witch_soya/article/details/7589816
VC控件自绘制三步曲的更多相关文章
- WPF 截图控件之绘制方框与椭圆(四) 「仿微信」
前言 接着上周写的截图控件继续更新 绘制方框与椭圆. 1.WPF实现截屏「仿微信」 2.WPF 实现截屏控件之移动(二)「仿微信」 3.WPF 截图控件之伸缩(三) 「仿微信」 正文 有开发者在B站反 ...
- WPF 截图控件之绘制箭头(五)「仿微信」
前言 接着上周写的截图控件继续更新 绘制箭头. 1.WPF实现截屏「仿微信」 2.WPF 实现截屏控件之移动(二)「仿微信」 3.WPF 截图控件之伸缩(三) 「仿微信」 4.WPF 截图控件之绘制方 ...
- Membership三步曲之进阶篇 - 深入剖析Provider Model
Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...
- Membership三步曲之入门篇 - Membership基础示例
Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 - Membership基础示例 Membership三步曲之进阶篇 - 深入剖析Pro ...
- [转]Membership三步曲之入门篇 - Membership基础示例
本文转自:http://www.cnblogs.com/jesse2013/p/membership.html Membership三步曲之入门篇 - Membership基础示例 Members ...
- 第七章 new的三步曲
这章是本系列文章的重点,这章揭示了js对象的真正本质 看下面的事例 var a = new b(); 等价于 ①var a={}; ②a.__proto__=b.prototype; ③b.call( ...
- 在Image控件中绘制文字
//Canvas 在Image控件中绘制文字 procedure TForm1.Button1Click(Sender: TObject);begin image1.Canvas.Font.Size ...
- SQL Server2005 表分区三步曲(zz)
前言 SQL Server 2005开始支持表分区,这种技术允许所有的表分区都保存在同一台服务器上.每一个表分区都和在某个文件 组(filegroup)中的单个文件关联.同样的一个文件/文件组可以容纳 ...
- 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .
双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后 ...
随机推荐
- struts2_11_实现自己的拦截器的定义
1)登录界面代码: <% //设置session的值keyword为user request.getSession().setAttribute("user", " ...
- iOS6 与iOS7以及7以上状态栏的颜色设置
iOS7默认状态栏文字颜色为黑色 修改为白色的方法:(chenyong注意 我的Status bar style 使用的仍是默认值Gray style(default)) 1在Info.plist中设 ...
- 对Java字符串的探究
问题的出发点 在网上看到一道题: 1 String str = new String("abc"); 以上代码执行过程中生成了多少个 String 对象? 答案写的是两个.&quo ...
- windown下linux子系统的安装和卸载
原文:windown下linux子系统的安装和卸载 安装 第一步 打开开发人员模式 第二步 勾选适用linux的window子系统 第三步 打开powershell 第四步 在PowerShe ...
- POJ1236-Network of Schools(Tarjan + 缩点)
主题链接 题意:给定一张有向图,问最少选择几个点能遍历全图.以及最少加入几条边使得有向图成为一个强连通图. 思路:对于有向图而言,首先求出有几个强连通分量,之后将每一个强连通分量缩点,形成DAG.本题 ...
- aspnetboilerplate && .net core 使用原生sql
利用aspnetboilerplate提供的工具类IDbContextProvider private readonly IDbContextProvider<XXXDbContext> ...
- iTerm - 让你的 MAC 命令行更加丰富多彩
今天要给大家介绍一个工具: iTerm.这次我们讲的和开发技术无关,只是一个工具.但虽然是一个工具,却能让你的功力提升一个层级.下面我们就来一看究竟.每一位开发者都难免要用到命令行,从 git 版本库 ...
- Emgu-WPF 激光雷达研究-定位实现
原文:Emgu-WPF 激光雷达研究-定位实现 特定位置或障碍物位置定位实现. 读取激光雷达数据并存储于本地作为测试数据.每一帧数据对同一障碍物的定位信息均存在偏差.所以先对需要定位的点进行数据取样. ...
- WPF Binding Path妙用
<Window x:Class="XamlTest.Window9" xmlns="http://schemas.microsoft.com/winf ...
- jquery动态操作元素
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...