Customizing the Appearance of CSliderCtrl Using Custom Draw

来源:互联网 发布:淘宝店铺分销 编辑:程序博客网 时间:2024/05/16 04:51

原文地址:

Customizing the Appearance of CSliderCtrl Using Custom Draw

这表明是非常简单的代码。我从CSliderCtrl派生一个类并增加了NM_CUSTOMDRAW

的反射通知消息的处理。VC++6.0可以从类向导为你自动实现或者从类视图:
点击增加消息处理然后选择=NM_CUSTOMDRAW;其中“=”等于符号非常重要因为
它告诉VC++增加一个反射消息处理到控件类里面而非到它的父类里面。你也
可以手动增加一个ON_NOTIFY_REFLECT宏在控件消息映射达到相同目的,紧接着
增加函数原型在头文件和实际的函数体在.cpp文件中(类向导会自动完成这些)
查看MSDN的"TN062: Message Reflection for Windows Controls"更详细的消
息反射介绍。我然后会增加一个简单的函数叫SetColor(COLORREF cr)用于
设置滑块的颜色。以下代码就是全部的类,从代码的简短度,你便可知我为什么
说相当简单。我们将在后面作个简单讨论:


那,这些代码如何运行呢?
首先,要掌握自绘关键点就是响应控件的几乎是一个的NM_CUSTOMDRAW通知消息。
基本上,每一次绘制循环的开始,控件都会发送NM_CUSTOMDRAW通知消息。假如该
通知消息被忽略(99.9%都会被忽略),那么控件在接下来就不会再发送
NM_CUSTOMDRAW通知(直到下一个绘制循环的开始)并且仅是简单的自动绘制标准
的外貌。
另外,假如你返回CDRF_NOTIFYITEMDRAW,那么控件接下来就会继续发送NM_CUSTOMDRAW
通知在绘制的每一步。具体来说,通用控件在每一步绘制自己(很像你或你
自己可能会设计它),而且一项(或者说一部分)都会发送。trackbar控件
例如,在三个独立的项绘制自己:trackbar滑块滑动的通道(标识为TBCD_CHANNEL)
,滑块(用户拖拽部分标识是TBCD_THUMB),以及在trackbar边界的递增
标记(标识为TBCD_TICS)。如此,我们对改变thumb外貌感兴趣,所以我们返回
CDRF_NOTIFYITEMDRAW在CDDS_PREPAINT期间,我们直到收到NM_CUSTOMDRAW
通知表明绘制thumb的预绘制状态已经到达。在这时,我们步入插入我们自己
的绘制代码。胜过让控件绘制它自己的thumb,我们绘制有色的椭圆在thumb
矩形绘制区域,然后返回CDRF_SKIPDEFAULT告诉控件不会再作任何绘制(
否则就会返回标准thumb覆盖我们自己绘制的)。
所有我们执行绘制需要的信息都包含着NMCUSTOMDRAW结构体中,通过
OnCustomDraw消息的第一个参数传进来。以下是NMCUSTOMDRAW结构体
typedef struct tagNMCUSTOMDRAWINFO {
    NMHDR hdr;
    DWORD dwDrawStage;
    HDC hdc;
    RECT rc;
    DWORD_PTR dwItemSpec;
    UINT uItemState;
    LPARAM lItemlParam;
} NMCUSTOMDRAW, *LPNMCUSTOMDRAW;
每个成员意义:
MemberName Meaning
hdr NMHDR结构体,包含该通知消息额外信息
dwDrawStage 当前绘制阶段。这或者是如CDDS_xxxxx表示控件
绘制阶段的全局值(Pre-erase,post-paint etc。)或者
是CDDS_ITEMxxxxx表示控件每个子项绘制阶段的子项值(
像TBCD_THUMB项的Pre-erase,或者TBCD_CHANNEL项的post-paint)
hdc 控件设备上下文句柄。使用该HDC执行任何的GDI功能
rc 描述被绘制的边界区域的RECT结构体
dwItemSpec TBCD_CHANNEL、TBCD_THUMB、TBCD_TICS之一的控件项标识
uItemState CDIS_xxxxx系列表明项的状态(像被勾选或置灰或获得焦点或选中)
lItemlParam 应用程序定义的所以同样控件特性项数据。


我们因此拥有HDC设备上下文可以绘制,而且拥有RECT说明在哪里绘制。另外,

NMCUSTOMDRAW结构体也告诉我们上面为提及的绘制阶段和哪项被绘制。


更多复杂例子:用户拖拽期间的反馈

包括一步步引导创建你自己的自绘CSliderCtrl

到我涉及为止,这是非常棒也非常少数量的自绘代码,我希望你的鼓动下继续走的更远。

首先我注意到当用户拖拽控件滑块时滑块没有发光。通过比较,标准控件的滑块在拖拽时会发光:

在标准轨道控件上,滑块在拖拽时是发亮的

在以上代码情况下,在拖拽时滑块没发亮

它好像在任意情况下用黑灰色随意绘制了滑块的边框。尽管当滑块的颜色接近深色时看起来很棒(上面展示的深蓝色滑块),但当滑块颜色是明亮时看起来就不好了。

环绕明亮色滑块的深灰色边框太灰暗了

解决在用户拖拽滑块时发现的第一个问题(使用NMCUSTOMDRAW结构体uItemState成员)通过绘制图案画刷代替实心画刷。图案画刷的位图案画刷可以使用uc801题为“cool GDI pattern brush tool with c++ source code”的工具创建。

第二个问题可以通过我在Shell Color Palette Handling Functions下单“Shell Lightweight Utility Functions”发现的三个光滑颜色坐标函数来解决。这些函数是ColorAdjustLuma,ColorHLSToRGB和ColorRGBToHLS以及MSDN的以下描述:

ColorAdjustLuma 改变RGB值得发光度。饱和度和色调不会受影响

ColorHLSToRGB 转换饱和度颜色到RGB格式

ColorRGBToHLS 转换RGB颜色到饱和度格式

基本上,这些函数转换标准RGB和饱和度,同时也调整色调和饱和度发光度。简短背景,RGB仅是众多不同颜色域可能中的一个(或者,颜色坐标系统)。以下其他颜色域例子包括CIELab,CIELUV,Jch,HLS和CIEXYZ。每个都有自己的优缺点。RGB是在处理在显示器上每个像素每个坐标匹配红绿蓝元素最好的颜色显示方案。但是当你试着从直觉和感官等虚拟方式思考颜色时很可怕。原因是RGB和其他颜色域比起来并非是非常好的感官颜色域。HLS是感官颜色域的一个例子(尽管还有很多其他更好的颜色域)。在HLS方面,假如你想要明亮色,简单增加发光度(“L”值)。你是否更想要一个耀眼颜色接近纯色又不是灰色?增加色调(“S”值)。想要紫罗兰和偏红的彩色?增加饱和角度(“H”值)。这样更直观,更接近我们思考颜色的方式。

我们可以使用以上三个RGB-to-HLS的颜色函数(事实上,我们仅仅需要第一个)很容易去创建明亮和灰暗颜色,我们现在就这样做,来创建我们用于图案画刷的明亮色,以及滑块边框阴影部分。

一步步引导创建你自己的自绘CSliderCtrl

让我们一起来写代码,这次用对话框(比第一个例子更便于观察)。现在这一步是使用VC++6.0 ,但是假如你使用其他IDEs你可以类似操作。

创建一个新的基于对话框的工程,我们命名为“HiliteSlider”,并且所有都默认。打开类向导,增加一个派生于CSliderCtrl的新类,称作“CCDSliderCtrl”。不要关闭类向导,打开消息映射标签,增加新的=NM_CUSTOMDRAW的消息处理,我们使用默认名字OnCustomDraw.接着,增加一个函数,,点击"OK"关闭类向导.现在离开空函数;我们稍后会填写它的框架.

主对话框资源模板应该在你面前(假如没有就切换过去).找到"TODO: Place controls here"标记提示,然后从toolbox插入一个轨道控件.采用默认的IDC_SLIDER1资源ID,改变为垂直 方向的控件.你的对话框资源应像这样:

再次打开类向导,进入主对话框类的"Member Variables"标签,双击IDC_SLIDER1以增加成员变量.给一个变量名,选择"Control"(而非"Value")作为类型,并点击下拉框选择你自己的CCDSliderCtrl类。点击“OK”按钮保存新的变量并关闭类向导。你需要增加CCDSliderCtrl类的.h头文件在主对话框头文件里(正像类向导警告你的一样)。打开CHiliteSliderDlg类的头文件(名字应该是HiliteSliderDlg.h)增加如下行:

#include “CDSliderCtrl.h”

在顶部。这样,如果你想,该工程可以编译和运行。但还有更多工作要做。

增加以下五个protected成员变量在CCDSliderCtrl类中:

COLORREF m_crPrimary, m_crShadow,m_crHilite;

CBrush m_nomalBrush,m_focusBrush;

像以下代码增加public 成员函数SetPrimaryColor,得到明亮和阴影颜色,创建实心画刷和图案画刷:


最后:

ON_NOTIFY_REFLECT:父窗口处理WM_NOTIFY消息只会在子控件发送该消息并且没有通过ON_NOTIFY_REFLECT()反射消息处理过

ON_NOTIFY_REFLECT_EX:你的消息处理可以也可以不让服窗口处理。假如处理函数返回FALSE,该消息就会被父窗口处理,而返回TRUE父窗口就不会处理。

注意:反射消息处理在通知消息之前被处理;轨道控件子项绘制顺序是:TBCD_TICS->TBCD_CHANNEL->TBCD_THUMB(最后发生绘制滑块的消息)


0 0
原创粉丝点击