职业IT人-IT人生活圈

 找回密码
 成为会员
搜索
查看: 1273|回复: 1

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

[复制链接]
不仅有点帅 发表于 2007-7-8 11:36 | 显示全部楼层 |阅读模式
 相信读者朋友们对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
//系统常用颜色的自定义名称
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 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:efWindowProc(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:rawItem(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();
   }
  }
 }
}
   四、小结

  上面的代码也适用于菜单等大多数控件的自画过程,其实本书在前面一些实例中也已经讲述了控件自画的内容,读者朋友们可以结合起来一起学习,相信一定能够把控件的自画这一内容掌握的一清二楚的。
话说我当年 发表于 2011-8-15 09:08 | 显示全部楼层
哈哈 OK ~~~
您需要登录后才可以回帖 登录 | 成为会员

本版积分规则

QQ|手机版|小黑屋|网站帮助|职业IT人-IT人生活圈 ( 粤ICP备12053935号-1 )|网站地图
本站文章版权归原发布者及原出处所有。内容为作者个人观点,并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是信息平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽造成漏登,请及时联系我们,我们将根据著作权人的要求立即更正或者删除有关内容。

GMT+8, 2024-4-20 14:46 , Processed in 0.154524 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表