龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

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

时间:2009-12-22 15:42来源:未知 作者:admin 点击:
分享到:
相信读者朋友们对Office2000一定非常熟悉吧,它里面的东东可真不少,不管是活泼可爱的"大眼夹",还是各种平面造型的Windows控件都很受广大用户喜欢。那么这次就让我们来模拟它做个十

  相信读者朋友们对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
   <!-- frame contents -->
<!-- /frame contents -->
   //系统常用颜色的自定义名称
   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::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();
      }
     }
    }
   }

      四、小结
  
     上面的代码也适用于菜单等大多数控件的自画过程,其实本书在前面一些实例中也已经讲述了控件自画的内容,读者朋友们可以结合起来一起学习,相信一定能够把控件的自画这一内容把握的一清二楚的。
  
精彩图集

赞助商链接