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 这样只包含三个成员(hwndFromidFrom 和 code)的结构。

因此,您通常需要将该结构指针转换为信息量更大的结构 — LPNMCUSTOMDRAWLPNMCUSTOMDRAW 指向 NMCUSTOMDRAW,它包含诸如 dwDrawStagedwItemSpec 和 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

时间: 2024-10-25 17:06:50

VC控件自绘制三步曲的相关文章

高效阅读文章的“三步曲”

高效阅读文章的“三步曲” 通读杨春玲老师的两篇博文“我科研过程中走过的弯路及纠偏探索 ”.“如何有效阅读文献(图) ”及其中链接的文章How to Read a Paper http://blog.sciencenet.cn/home.php?mod=attachment&filename=howtoread.pdf&id=47254,现给出自己对于这一问题的思考,以下没有标注的引号里的内容均来自杨老师的两篇博文中. 一.认真研读自己专业的经典教材,“教材是一个领域里最佳参考,研究之前先查

企业核心人才培养三步曲

信息的价值,思考的价值,学习的价值,连接的价值,时间的价值,无形的价值,人才需要不断灌输有用的信息. 人是企业最宝贵的资源,是企业巨大的财富,更是企业核心竞争力的资本,企业核心人才决定企业是否能成为百年老店?基业长青.因此,稳定核心人才,培育核心人才是企业家和企业人力资源管理者的核心战略任务,需长抓不懈.IBM前任总裁郭仕纳曾经说过:"21世纪获得成功的企业,将会是那些尽力开发.培育核心人才的组织". 一.什么是企业核心人才? 要培养好企业核心人才,企业就要建立和制定核心人才标准.笔者

如何在双向绑定的Image控件上绘制自定义标记(wpf)

原文:如何在双向绑定的Image控件上绘制自定义标记(wpf) 我们的需求是什么? 答:需要在图片上增加一些自定义标记,例如:2个图片对比时,对相同区域进行高亮. 先上效果图: 设计思路 1.概述 1.通过TargeUpdated事件,重新绘制图片进行替换. 2.详细实现 1.我们先绑定ImageTargetUpdated事件. ? 1 <Image x:Name="DestImageControl" Source="{Binding Path=Source.Url,

在Image控件中绘制文字

//Canvas 在Image控件中绘制文字 procedure TForm1.Button1Click(Sender: TObject);begin  image1.Canvas.Font.Size:= 72; //设置文字大小  image1.Canvas.TextOut(1,1,'Delphi'); //输出文字end; 来自为知笔记(Wiz) 在Image控件中绘制文字

OpenCV-2.4.2 安装三步曲

注意:本人未使用  ffmpeg 的全部依赖库,比如AAC 音频编码库(libfaac-dev),MP3 编码库(ibmp3lame-dev),具体的配置为: ./configure --enable-shared --enable-gpl --enable-version3 --enable-nonfree --enable-x11grab --enable-libx264 --enable-libxvid ===========================================

LAMP环境配置三步曲之(一) CentOS 编译安装 Apache

LAMP环境的配置现今虽然已比之前大大的简化了,但对于一些不熟悉Linux系统的朋友来说,还是有一定难度的,这里将本人的配置过程记录下来,希望能对大家有一些帮助. 本期介绍CentOS下编译安装Apache的方法: 1. 下载Apache服务器 httpd-2.2.26 wget http://apache.fayea.com/apache-mirror//httpd/httpd-2.2.26.tar.gz 2. 安装gcc等必须的编译器 yum install autoconf automak

[转]Membership三步曲之入门篇 - Membership基础示例

本文转自:http://www.cnblogs.com/jesse2013/p/membership.html Membership三步曲之入门篇 - Membership基础示例 Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 -  Membership基础示例 Membership三步曲之进阶篇 -  深入剖析Provider model Membership三步曲之高级篇 -  从Membership 到 .NET 4.5 之 A

Office操作:图文混排三步曲

我们在编辑Word文档的时候,图片和文字的混排编辑可能是我们遇到的最为主要的情况,恰到好处的图文混排不仅可以起到美化文档的效果,对于阅读者阅读并理解文档内容也是大有裨益.因此,熟练掌握Word的图文混排是我们制作比如海报.杂志.刊物.贺卡等这类非纯文本文档的第一步,今天,我在这给大家介绍图文混排的基本知识及技巧,先给大家展示一个效果图. Office操作:图文混排三步曲效果展示准备工作:1.准备好文字材料:2.准备好图片素材:3.构思好排版效果. 图文混排第一步:在文档中插入图片.方法:通过[插

VC 对话框背景颜色、控件颜色(三种方法)

系统环境:Windows 7软件环境:Visual C++ 2008 SP1本次目的:为对话框设置背景颜色.控件颜色 既然MFC对话框不好开发,那么现在我们来开始美化我们的对话框.为对话框设置背景颜色.控件颜色等等. 对话框背景颜色: 网上流传有四种方法(可能还不止),在VC++2008SP1测试后,发现只有三种可以使用了,其中第一种被废弃了.以下是四种方法: 方法一 (失效):调用CWinApp类的成员函数SetDialogBkColor来实现.       其中函数的第一个参数指定了背景颜色