113E博章

       登录 /注册
首页 一算子网 发布
分类 :c++/vc

在VC+应用程序中实现颜色选择组合框

[ 2007-6-10 23:56:00 | 发表者 : zihe ]

  相信读者朋友们对office2000一定非常熟悉吧,它里面的东东可真不少,不管是活泼可爱的"大眼夹",还是各种平面造型的windows控件都很受广大用户喜欢。那么这次就让我们来模仿它做个十分常用的控件:"颜色组合框"。如果你现在正在做关于字处理类的软件时这个"东东"一定对你有用。程序编译运行后的界面效果如图一所示:


图一、颜色选择组合框

  一、实现方法

  首先让我们先来了解一下画控件的基本原理和过程,也许这个才是本文的原意。大家都知道windows中所有可视的东西都是画出来的,那么这个画画的内部过程又是怎样的呢?一般画windows控件的过程分为三大部分:一是在wm_measureitem消息影射函数中设置当前要画的item的大小尺寸;二是在wm_drawitem消息影射函数中根据item的大小尺寸来画该item(图标/位图/字符串等);三是在wm_paint消息映射函数中不断的绘制当前的控件内容。下面我们针对cbscolorcombobox类的这几个过程来做个简单的介绍:

  在wm_measureitem消息影射函数中设定item的大小尺寸的时候,我们只需要设置item的高度即可。这里的高度我们设置为2倍的系统小图标(small icon)的高度,其尺寸用::getsystemmetrics(sm_cxsmicon)取得。

  visual c++的程序开发人员可以在item的矩形区域内画各种各样的信息,例如:图标/位图/字符串等等。那么有人会疑问:"我们用什么来画?我们在哪里画?又如何来画呢?"。答案其实都在这个lpdrawitemstruct结构中。hdc成员为设备上下文环境(hdc),获得了该设备句柄也就意味着我们拥有了画任何位图/图标/文本的能力;那么接下来的问题就是:我们在哪里来画呢?答案也很简单:获得lpdrawitemstruct结构中item的矩形区域(rcitem),那么这就是你施展才华的空间了,要充分利用它哦!

  最后一步就是如何来画的问题了,说白了就是如何分配每个元素的空间,如何在它们各自的空间上画出你想要的东西。按照常规一般分别计算出icon所占的矩形区域/文本所占的矩形区域/位图的矩形区域,如果你还有其他元素那么也应该计算出该元素所占的矩形区域/位图所占的矩形区域。接下来的一切都很简单了,不外乎cdc类的几个常用函数:画图标用drawicon()、画位图用bitblt()、画文字用drawtext()等函数。如果你觉得视觉上还不够cool,你还可以来设置各个item的文本颜色,背景颜色,以及图标的突起和凹陷的视觉效果。

  不过在上述过程中需要注意三个问题,一是为了消除不断绘制所带来的闪烁现象,需要在wm_erasebkgnd消息响应中作些特殊处理;在wm_paint消息中直接把组合框的客户区当成一幅位图来不断更新,而不是对icon区域和文本区域分别重绘。二是每当用户改变了组合框的当前内容后,在画新的item之前一定要记得清除前次组合框内的内容。三是如果想选择更多的颜色,那么只要选择组合框中的最后一个item(more colors)即可,这个item是为用户自定义颜色而专门设置的。

  二、编程步骤

  1、启动visual c++6.0,生成一个基于对话框的项目,将该项目命名为"ww";

  2、使用class wizard新建一个类cbscolorcombobox,其基类选择为ccombobox类;

  3、在程序的对话框中放置一个combobox控件,使用classwizard添加相应的ccombobox类成员变量,然后将该成员变量的类型修改为cbscolorcombobox;

  4、添加代码,编译运行程序。

  三、程序代码

//////////////////////////////////////////////////////////////cbscolorcombobox类的头文件;
#if !defined(_bs_bscolorcb)
#define _bs_bscolorcb
#include <afxtempl.h>
//系统常用颜色的自定义名称
const static char* strcolorname[] =
{
 "crscrollbar","crbackground","cractivecaption", "crinactivecaption", "crmenu", "crwindow", "crwindowframe",  "crmenutext", "crwindowtext", "crcaptiontext", "cractiveborder","crinactiveborder", "crappworkspace",  "crhighlight", "crhighlighttext", "crbtnface", "crbtnshadow", "crgraytext", "crbtntext",  "crinactivecaptiontext",
 "crbtnhighlight","cr3ddkshadow", "cr3dlight", "crinfotext", "crinfobk",
 "crhotlight","crgradientactivecaption", crgradientinactivecaption"
};

typedef struct bscbitem
{
 int iindex;
 colorref crcolor;
 lpctstr lpcaption;
}bscbitem, *lpbscbitem;

class cbscolorcombobox : public ccombobox
{
 declare_dyncreate(cbscolorcombobox)
 public:
  cbscolorcombobox();
  virtual ~cbscolorcombobox();
  bool create(dword dwstyle, const rect& rect, cwnd* pparentwnd, uint nid);
  //初始化组合框(第一个被调用的函数)
  void initbscolorcb(void);
  //得到当前的颜色值或r/g/b值
  colorref getcolor();
  void getrgbvalue(int* r, int* g, int* b);
 public:
  //{{afx_virtual(cbscolorcombobox)
   public:
    virtual void drawitem(lpdrawitemstruct lpdrawitemstruct);
    virtual void measureitem(lpmeasureitemstruct lpmeasureitemstruct);
  //}}afx_virtual
 protected:
  bool m_bovercontrol; //鼠标的状态(是否处于按钮上)
  int iiconx, iicony; //small icon的大小尺寸
  colorref m_crcolor; //当前选中的颜色
  clist<lpbscbitem, lpbscbitem> m_critem;

  void oncbpaint(cdc* pdc);
  lpbscbitem getitem(int iindex = 0);
 protected:
  //{{afx_msg(cbscolorcombobox)
   afx_msg bool onerasebkgnd(cdc* pdc);
   afx_msg void onpaint();
   afx_msg void ontimer(uint nidevent);
   afx_msg void onmousemove(uint nflags, cpoint point);
   afx_msg void onselchange();
   afx_msg void onselendok();
  //}}afx_msg
  declare_message_map()
};
#endif // !defined(_bs_bscolorcb)

///////////////////////////////////////////////////////////////cbscolorcombobox的实现文件;
#include "stdafx.h"
#include "bscolorcombobox.h"
cbscolorcombobox::cbscolorcombobox()
{
 //当前鼠标是否在对象上
 m_bovercontrol = false;
 //小图标尺寸
 iiconx = ::getsystemmetrics(sm_cxsmicon);
 iicony = ::getsystemmetrics(sm_cysmicon);
}

cbscolorcombobox::~cbscolorcombobox()
{
 while(!m_critem.isempty())
 {
  lpbscbitem lpitem = m_critem.removehead();
  delete lpitem;
 }
}

bool cbscolorcombobox::create(dword dwstyle, const rect& rect, cwnd* pparentwnd, uint nid)
{
 dword dw = dwstyle;

 if( !ccombobox::create(dw, rect, pparentwnd, nid) )
  return false;
 cfont * font = cfont::fromhandle((hfont)::getstockobject(default_gui_font));
 setfont(font);

 return true;
}
implement_dyncreate(cbscolorcombobox, ccombobox)

begin_message_map(cbscolorcombobox, ccombobox)
//{{afx_msg_map(cbscolorcombobox)
 on_wm_erasebkgnd()
 on_wm_paint()
 on_wm_timer()
 on_wm_mousemove()
 on_control_reflect(cbn_selchange, onselchange)
 on_control_reflect(cbn_selendok, onselendok)
//}}afx_msg_map
end_message_map()

void cbscolorcombobox::initbscolorcb(void)
{
 int imincolor = color_scrollbar,
 imaxcolor = color_btnhighlight;
 if(winver >= 0x0400)
  imaxcolor = color_infobk;
 if(winver >= 0x0500)
  imaxcolor = 28;
 //初始化cb颜色列表框的item(常见的syscolor值)
 for(int iloop = imincolor; iloop <= imaxcolor; ++iloop)
 {
  lpbscbitem lpitem = new bscbitem;
  lpitem->iindex = addstring(strcolorname[iloop]);
  lpitem->crcolor = ::getsyscolor(iloop);
  lpitem->lpcaption = strcolorname[iloop];
  //
  if(m_critem.isempty())
   m_critem.addhead(lpitem);
  else
   m_critem.addtail(lpitem);
 }
 //该item是为了用户自定义颜色而设置
 lpbscbitem lpitem = new bscbitem;
 lpitem->iindex = addstring("more colors");
 lpitem->crcolor = rgb(213, 233, 249);
 lpitem->lpcaption = "more colors";
 if(m_critem.isempty())
  m_critem.addhead(lpitem);
 else
  m_critem.addtail(lpitem);
  //初始化当前颜色
 m_crcolor = m_critem.gethead()->crcolor;
}

bool cbscolorcombobox::onerasebkgnd(cdc* pdc)
{
 assert(pdc->getsafehdc());
 return false;
}

void cbscolorcombobox::onpaint()
{
 cpaintdc dc(this);
 oncbpaint(&dc);
}

void cbscolorcombobox::oncbpaint(cdc* pdc)
{
 assert(pdc->getsafehdc());

 //绘制客户区
 cdc dmemdc;
 dmemdc.createcompatibledc(pdc);
 dmemdc.setmapmode(pdc->getmapmode());
 //画动作
 cbitmap mnewbmp;
 rect rc;
 getclientrect(&rc);
 mnewbmp.createcompatiblebitmap(pdc, rc.right - rc.left, rc.bottom - rc.top);
 cbitmap* poldbmp = dmemdc.selectobject(&mnewbmp);
 //子类可以以friend方式来访问父类的protected成员变量和函数
 cwnd::defwindowproc(wm_paint, (wparam)dmemdc.m_hdc, 0);
 pdc->bitblt(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, &dmemdc,rc.left ,rc.top, srccopy);
 //恢复
 dmemdc.selectobject(poldbmp);
 poldbmp->deleteobject();
 dmemdc.deletedc();
 getwindowrect(&rc);
 screentoclient(&rc);
 pdc->drawedge(&rc, (m_bovercontrol ? bdr_raisedinner: bdr_sunkeninner), bf_rect);
}

void cbscolorcombobox::ontimer(uint nidevent)
{
 if(nidevent == 888 && iswindowenabled())
 {
  cpoint point;
  ::getcursorpos(&point);
  crect rect;
  getwindowrect(&rect);
  if(rect.ptinrect(point))
  {
   m_bovercontrol = true;
  }
  else
  {
   m_bovercontrol = false;
   killtimer(nidevent);
  }
 }
 ccombobox::ontimer(nidevent);
}

void cbscolorcombobox::measureitem(lpmeasureitemstruct lpmeasureitemstruct)
{
 lpmeasureitemstruct->itemheight = iicony + 5;
}

void cbscolorcombobox::drawitem(lpdrawitemstruct lpdis)
{
 assert(lpdis->ctltype == odt_combobox);
 //画笔
 cdc* pdc = cdc::fromhandle(lpdis->hdc);
 assert(pdc->getsafehdc());
 //绘制区
 rect rc = lpdis->rcitem;
 rect rcicon(rc), rctxt(rc);
 //当前的item索引号
 lpbscbitem lpitem = getitem(lpdis->itemid);
 if(lpitem != null)
 {
  //画颜色icon
  rcicon.right = rcicon.left + iiconx;
  rcicon.top += (rc.bottom - rc.top - iicony) / 2;
  rcicon.bottom = rcicon.top + iicony;
  pdc->fillsolidrect(rcicon.left, rcicon.top,
  rcicon.right - rcicon.left, rcicon.bottom - rcicon.top, lpitem->crcolor);
  pdc->drawedge(&rcicon, bdr_raisedinner, bf_rect);
  //开始画文字
  int noldbkmode = pdc->setbkmode(transparent);
  pdc->settextcolor(rgb(0, 0, 0));
  rctxt.left = rcicon.right + 5;
  rctxt.top = rcicon.top;
  pdc->drawtext(lpitem->lpcaption, &rctxt,
  dt_vcenter | dt_end_ellipsis | dt_noclip | dt_singleline);
  pdc->setbkmode(noldbkmode);
 }
}

void cbscolorcombobox::onmousemove(uint nflags, cpoint point)
{
 m_bovercontrol = true;
 settimer(888, 100, null);
 ccombobox::onmousemove(nflags, point);
}

lpbscbitem cbscolorcombobox::getitem(int iindex)
{
 //当前的item索引号
 position pos = m_critem.findindex(iindex);
 if(pos)
 {
  lpbscbitem lpitem = m_critem.getat(pos);
  assert(lpitem);
  return lpitem;
 }
 else
  return (lpbscbitem)null;
}

colorref cbscolorcombobox::getcolor()
{
 if(iswindowenabled())
  return m_crcolor;
 else
 {
  return (m_crcolor = getitem(this->getcursel())->crcolor);
 }
}

void cbscolorcombobox::getrgbvalue(int* r, int* g, int* b)
{
 *r = getrvalue((dword)m_crcolor);
 *g = getgvalue((dword)m_crcolor);
 *b = getbvalue((dword)m_crcolor);
}

void cbscolorcombobox::onselchange()
{
 int iindex = getcursel();

 if(iindex != cb_err && iindex >= 0)
 {
  cdc* pdc = this->getdc();
  //绘制区
  rect rc;
  int iscrollx = ::getsystemmetrics(sm_cxvscroll);
  getclientrect(&rc);
  pdc->fillsolidrect(rc.left + 2, rc.top + 2, rc.right - rc.left - iscrollx - 4, rc.bottom - rc.top - 2,
::getsyscolor(color_window));
  rect rcicon(rc), rctxt(rc);
  //当前的item索引号
  lpbscbitem lpitem = getitem(iindex);
  if(lpitem != null)
  {
   m_crcolor = lpitem->crcolor;

   //画颜色icon
   rcicon.left += 2;
   rcicon.right = rcicon.left + iiconx;
   rcicon.top += (rc.bottom - rc.top - iicony) / 2;
   rcicon.bottom = rcicon.top + iicony;
   pdc->fillsolidrect(rcicon.left, rcicon.top,
   rcicon.right - rcicon.left, rcicon.bottom - rcicon.top, lpitem->crcolor);
   pdc->drawedge(&rcicon, bdr_raisedinner, bf_rect);
   //开始画文字
   int noldbkmode = pdc->setbkmode(transparent);
   pdc->settextcolor(rgb(0, 0, 0));
   rctxt.left = rcicon.right + 5;
   rctxt.top = rcicon.top;
   cfont* font = cfont::fromhandle((hfont)::getstockobject(default_gui_font));
   pdc->selectobject(font);
   pdc->drawtext(lpitem->lpcaption, &rctxt,
   dt_vcenter | dt_end_ellipsis | dt_noclip | dt_singleline);
   pdc->setbkmode(noldbkmode);
  }
  pdc->deletedc();
 }
}

void cbscolorcombobox::onselendok()
{
 int iindex = this->getcursel();
 lpbscbitem lptmpitem = getitem(iindex);
 if(lptmpitem != null)
 {
  if(lptmpitem->lpcaption == "more colors")
  {
   ccolordialog crdlg(rgb(255, 0, 0), cc_fullopen);
   int iret = crdlg.domodal();
   if(iret == idok)
   {
    m_crcolor = crdlg.getcolor();
    lpbscbitem lpitem = m_critem.gettail();
    assert(lpitem);
    lpitem->crcolor = m_crcolor;
    invalidate();
   }
  }
 }
}

  四、小结

  上面的代码也适用于菜单等大多数控件的自画过程,其实本书在前面一些实例中也已经讲述了控件自画的内容,读者朋友们可以结合起来一起学习,相信一定能够把控件的自画这一内容掌握的一清二楚的。
上一篇:C+内存对象大会战   
下一篇:C+箴言:确保公开继承模拟“is-a”

浏览模式 : 显示全部 | 评论 : 0 | 排序 | 浏览 : 124

我要发表文章
回复标题:  
    Email :
* 请输入验证码
 
统计数据
文章数量 : 300297
评论次数 : 460
访问次数 : 19295355
在线人数 : 2056

附近文章
  • VC+动态链接库编程之非MF...
  • VC+动态链接库编程之MFC规...
  • VC+动态链接库编程之读者...
  • 英国投票否决C+/CLI 微软...
  • C+内存对象大会战
  • C+箴言:确保公开继承模拟...
  • VC+编程实现对火焰的计算...
  • 简单SNMP管理程序的VC+代...
  • 用C+实现跨平台游戏开发之...
  • C+箴言:避免覆盖通过继承...
热门文章
  • 如...
  • Socket编程中select()的妙...
  • 如何在DLL中创建窗体(对话...
  • 重载赋值运算符 C+/VC
  • 展现TRACE之威力 C+/VC
  • ·用豪杰大眼睛制作照片v...
  • 一個簡單的錄音放音程序 ...
  • 用Socket接口实现网络异步...
  • MFC动态创建控件的消息处...
  • 实现服务器端的多线程SOC...
最新文章
  • 股票技术不准了
  • 2009年元旦放假安排
  • 三国 旬彧VS诸葛亮
  • ASR浮筹比例
  • 杨百万投资股票的三大秘诀...
最近评论
  • xueshanfh
  • 匿名
  • 匿名
  • 匿名
  • 匿名
  • 匿名Martha Jack
  • 匿名
  • Tom Moddy
  • 匿名Jimmy Adams
  • 匿名
 
 

Copyright (C) 一算子网 2006-2008, All Rights Reserved 京ICP备06020154号
服务QQ: 有问题请留言 留言邮箱pinhu530@sohu.com