在VC++應用程序中實現顏色選擇組合框

 相信讀者朋友們對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();
   }
  }
 }
}

  四、小結

  上面的代碼也適用於菜單等大多數控件的自畫過程,其實本書在前面一些實例中也已經講述了控件自畫的內容,讀者朋友們可以結合起來一起學習,相信一定能夠把控件的自畫這一內容掌握的一清二楚的。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章