1. 程式人生 > >自繪ComboBox改變顏色和大小

自繪ComboBox改變顏色和大小

要點

      1. ComboBox控制元件由三部分組成:ComboBox本身,CEidt或者CStaitc,ClistBox。當型別是Dropdown時,內部是CEdit;是型別是Drop List時,內部是Static。

      2. ComboBox支援自繪需要修改Owner Draw屬性為Fixed(固定的列表寬度和高度) or Variable(可變的列表寬度和高度)。響應WM_DRAWITEM(反射OCM_DRAWITEM)訊息,過載DrawItem函式對下拉列表元素進行重繪。在DrawItem中需要用到GetLBText函式來獲取要繪製的文字資訊,這就需要增加Has Strings

屬性。

      3. 改變ComboBox列表寬度高度,是通過響應WM_MEASUREITEM(發射OCM_MEASUREITEM)訊息,過載MeasureItem函式,修改LPMEASUREITEMSTRUCT成員來達到效果。有這麼短英文原話(出處忘了是哪了) 
      For “fixed” type owner draw list box or combo box, WM_MEASUREITEM is sent when it is first created and the returned size will be used for all items. For “variable” type owner-draw list box or combo box, this message is sent for each item separately.

  
      意思就是如果屬性是“Fixed”,WM_MESUREITEM訊息在控制元件第一次建立的時候觸發,過載函式MesureItem返回的LPMEASUREITEMSTRUCT成員中的寬度和高度將被用作列表中的所有條目。如果屬性是“Variable”,列表中每一項分別響應。 
      這句話容易被忽略掉一種情況,如果是通過子類化(DDX_Control,SubclassDlgItem或SubclassWindow)來改變已有的控制元件,該控制元件又是“Fixed”。則過載的MeasureItem將不起作用。因為在子類化之前,建立的時候已經觸發過了。所以要想改變列表寬度和高度,要這麼設定
:  
      1) 通過子類化關聯控制元件:屬性要設定為“Variable”,下拉列表包含幾個條目,MeasureItem就會被呼叫幾次,改變每一項的的寬度高度,但是不能文字框(LPMEASUREITEMSTRUCT成員itemID = -1)的寬度高度 
      2) 呼叫Create動態建立:屬性設定為“Fixed”,MeasureItem會被呼叫兩次,LPMEASUREITEMSTRUCT成員itemID為-1和1。等於-1的時候可以改變文字框寬度高度。等於1的時候,被設定的寬度高度將應用於下拉列表所有項;屬性設定為“Variable”,MeasureItem會被呼叫>2次(列表條目個數+1)。同樣的,等於itemID=-1時候改變文字框,itemID=0,1,2...改變列表每一項各自的寬度高度。(如果發現點選下拉按鈕出現不了列表框,請檢查呼叫Create函式時候穿進去的Rect的高度是不是太小了)

      4. 改變顏色: 
      ● 改變CEdit顏色(Dropdown型別):只要響應WM_CTLCOLOREDIT訊息,在響應函式中返回新的畫刷; 
      ● 改變下拉列表顏色:在DrawItem過載函式中,根據Item狀態,繪製顏色。

     5. 子類化各個組成控制元件,可分別響應WM_CTLCOLORLISTBOX訊息[子類化listbox控制元件-lparam為hwnd-注意判斷是否為combox本身];子類化edit則用WM_CTLCOLOREDIT訊息,引數同listbox;取出hwnd即可;

實踐

      新建一個ComboBox派生類CColorComboBox,並且繼承CownerDraw,MSG_MAP中新增自繪的訊息鏈,父視窗中MSG_MAP中新增訊息反射巨集 REFLECT_NOTIFICATIONS()

  1: class CColorComboBox
  2:     : public CWindowImpl<CColorComboBox, CComboBox>
  3:     , public COwnerDraw<CColorComboBox>
  4: {
  5: public:
  6:     CColorComboBox(){}
  7:     ~CColorComboBox(){}
  8: 
  9: protected:
 10:     BEGIN_MSG_MAP(CColorComboBox)
 11:         CHAIN_MSG_MAP_ALT(COwnerDraw<CColorComboBox> ,1)
 12:         DEFAULT_REFLECTION_HANDLER()
 13:     END_MSG_MAP()
 14: }

      如果是子類化,在介面設計器中新增一個ComboBox控制元件,修改其Owner Draw屬性為“Variable”,Has Strings屬性設定為“true”。在CColorComboBox類中,過載SubclassWindow函式:

  1:     BOOL SubclassWindow(HWND hWnd)
  2:     {
  3:         ATLASSERT(hWnd);
  4:         ATLASSERT(::IsWindow(hWnd));
  5: 
  6:         BOOL bRet = __super::SubclassWindow(hWnd);
  7:         if ( bRet )
  8:             _Init();
  9: 
 10:         ATLASSERT(GetStyle() & CBS_OWNERDRAWVARIABLE);
 11:         ATLASSERT(GetStyle() & CBS_HASSTRINGS);
 12: 
 13:         return bRet;
 14:     }

    如果是動態建立,過載Create函式:

  1:     HWND Create(HWND hWndParent, _U_RECT rect, _U_MENUorID MenuOrID = 0U)
  2:     {
  3:         HWND hWnd = __super::Create(hWndParent, rect, NULL, 
  4:             WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWN /*or CBS_DROPDOWNLIST*/
  5:             | CBS_HASSTRINGS | CBS_OWNERDRAWFIXED/* or CBS_OWNERDRAWVARIABLE*/, 
  6:             0, MenuOrID, NULL);
  7: 
  8:         if ( hWnd )
  9:             _Init();
 10: 
 11:         return hWnd;
 12:     }

      自繪下拉類表,過載DrawItem函式:

  1:     void DrawItem(LPDRAWITEMSTRUCT lpdis)
  2:     {
  3:         if ( (long)lpdis->itemID == -1 )
  4:             return;
  5: 
  6:         CDCHandle dc(lpdis->hDC);
  7:         CRect rcItem(lpdis->rcItem);
  8:         CString strItem;
  9:         GetLBText(lpdis->itemID, strItem);
 10: 
 11:         dc.SetBkMode(TRANSPARENT);
 12: 
 13:         BOOL bSelected = (lpdis->itemState & ODS_SELECTED) ? TRUE : FALSE;
 14:         if ( bSelected )
 15:         {
 16:             dc.FillSolidRect(rcItem, RGB(238, 238, 238));
 17:             dc.SetTextColor(RGB(0, 0, 0));
 18:         }
 19:         else
 20:         {
 21:             dc.FillSolidRect(rcItem, RGB(218, 218, 218));
 22:             dc.SetTextColor(RGB(255, 255, 255));
 23:         }
 24: 
 25:         dc.DrawText(strItem, strItem.GetLength(), rcItem, DT_LEFT | DT_SINGLELINE);
 26:     }

      改變寬度高度,過載MeasureItem函式:

  1:     void MeasureItem(LPMEASUREITEMSTRUCT lpmis)
  2:     {
  3:         if ( (long)lpmis->itemID == -1 )    
  4:             lpmis->itemHeight = 15;  // 文字框(對子類化無效)
  5:         else
  6:             lpmis->itemHeight = 40;  // 列表
  7:     }

      改變CEdit顏色,響應WM_CTLCOLOREDIT訊息,返回新的畫刷:

  1:     HBRUSH OnCtlColorEdit(CDCHandle dc, CEdit edit)
  2:     {
  3:         dc.SetBkMode(TRANSPARENT);
  4:         dc.SetTextColor(RGB(255, 0, 0));
  5:         
  6:         HBRUSH hBrush = ::CreateSolidBrush(RGB(216, 216, 216));
  7:         return hBrush;
  8:     }

    最終效果:

ab