1. 程式人生 > >MFC 自繪listbox , 自繪ComboBox 下拉列表框 如何設定其列表項的高度和寬度?

MFC 自繪listbox , 自繪ComboBox 下拉列表框 如何設定其列表項的高度和寬度?

自繪ListBox 設定其列表項的高度和寬度可以通過過載虛擬函式 MeasureItem (LPMEASUREITEMSTRUCT lpMeasureItemStruct) 來實現:

void CMyComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{

	// TODO:  Add your code to determine the size of specified item
	lpMeasureItemStruct->itemHeight = 25;  // 設定列表項的高度為25pixel
	lpMeasureItemStruct->itemWidth = 500;  // 設定列表項的寬度為500pixel , 但是這裡如果你的應用程式的寬度沒這麼多的話,它的寬度也不會是500
}


注意事項<1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

重繪listbox時遇到的問題,開始在OnMeasureItem裡做的操作,結果掛了。。。在MeasureItem裡OK。
CSDN上達人的解釋。。。清楚了。

OnMeasureItem will be called only if the control's class is created at run time, 
or it is created with the LBS_OWNERDRAWVARIABLE or CBS_OWNERDRAWVARIABLE style. 
If the control is created by the dialog editor, OnMeasureItem will not be called. 
This is because the WM_MEASUREITEM message is sent early in the creation process of the control. 
If you subclass by using DDX_Control, SubclassDlgItem, or SubclassWindow, 
the subclassing usually occurs after the creation process. 
Therefore, there is no way to handle the WM_MEASUREITEM message in the 
control's OnChildNotify function, which is the mechanism MFC uses to implement 
ON_WM_MEASUREITEM_REFLECT.

另一個的解釋是OnMeasureItem是響應子控制元件的WM_MEASUREITEM訊息的,MeasureItem是響應反射的自己傳送的WM_MEASUREITEM訊息的。

注意事項<2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

listctrl的自繪,不是很簡單,可能比CRichEditCtrl還要複雜些,遇到第一件事情是自適應改變行高,專案需要,當然也可以是固定,最終解決方案如下:

網上修改CListCtrl項高度的方法一般是擴大字型,及用圖片將項高度撐大.
這兩種方法雖然簡單,但是效果卻不是很理想.一種比較理想的方法是自畫CListCtrl,不過方法相對來說比較複雜.
要修改CListCtrl的列表項高度,我們需要自己新增 MeasureItem 的訊息響應函式,對應的訊息是 WM_MEASUREITEM+WM_REFLECT_BASE, 而不是 WM_MEASUREITEM.在CListBox裡我們可以直接在 ClassWizard 裡將此訊息響應新增進 class 裡,但是 CListCtrl 預設是沒有這個訊息響應的,我們需要手動新增它(注意,這裡不是 WM_MEASUREITEM. CListCtrl 僅有 WM_MEASUREITEM, 對應的函式為 OnMeasureItem).
為了響應這個訊息,我們還需要給列表加上 LVS_OWNERDRAWFIXED 風格.可以在 Create 列表的時候新增,也可以在 PreCreateWindow 虛擬函式中新增.
新增 MeasureItem 訊息響應函式,首先我們需要在類的標頭檔案中新增:
afx_msg void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
來宣告此訊息響應函式;
然後在cpp的訊息響應巨集中新增:
ON_WM_MEASUREITEM_REFLECT()
最後自己建立 MeasureItem 的函式定義:
/////////////////////////////////////////////////////////////////////////////
// CListEx message handlers
void CListEx::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if( m_nItemHeight > 0 )
   lpMeasureItemStruct->itemHeight = m_nItemHeight;
}
其中 m_nItemHeight 是我在標頭檔案中宣告的一個成員變數,用於從外部修改列表項高度.
然後我們新增一個方法,便於從外部直接修改列表項高度:
//設定行高
void SetItemHeight(UINT nHeight);
然後是該方法的定義:
//設定行高
void CListEx::SetItemHeight(UINT nHeight)
{
m_nItemHeight = nHeight;
CRect rcWin;
GetWindowRect(&rcWin);
WINDOWPOS wp;
wp.hwnd = m_hWnd;
wp.cx = rcWin.Width();
wp.cy = rcWin.Height();
wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);
}
這個方法的最後,使用了 SendMessage 傳送 WM_WINDOWPOSCHANGED 訊息讓 CListCtrl 進入 MeasureItem 的訊息響應函式,對列表高度進行修改.
因為我們這裡使用了列表的自繪風格,因此列表項需要自己繪製.
首先在類的宣告中新增 DrawItem 虛擬函式宣告:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
然後自畫 CListCtrl:
void CListEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
int    nItem = lpDrawItemStruct->itemID;
CDC*   pDC   = CDC::FromHandle(lpDrawItemStruct->hDC);
......
CRect   rcBound, rcLabel, rcIcon;
//獲得列表項圖示,標籤,及項的區域
GetItemRect ( nItem, rcIcon, LVIR_ICON );
GetItemRect ( nItem, rcLabel, LVIR_LABEL );
GetItemRect ( nItem, rcBound, LVIR_BOUNDS );
......
}
現在這個 CListCtrl 的過載類就支援自定義列表項高度了

原型解釋>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

MEASUREITEMSTRUCT結構體通知系統自繪控制元件或選單項的尺寸.這充許系統正確的處理使用者與控制元件的相互動作
typedef struct tagMEASUREITEMSTRUCT {   // mis
    UINT  CtlType;      // 控制元件型別
    UINT  CtlID;        // combo box, list box, or button 識別符號
    UINT  itemID;       // menu item, variable-height list box, 選單項,可變高的列表框
                        // or combo box identifier 或組合框的識別符號
    UINT  itemWidth;    // width of menu item, in pixels 選單項的寬,單位:象素
    UINT  itemHeight;   // height of single item in list box menu,
                        // in pixels 選單或列表框單一專案的高,單位象素
    DWORD itemData;     // application-defined 32-bit value 應用程式定義的32位值
} MEASUREITEMSTRUCT;

CtlType:
 指定控制元件的型別.這個成員可以是下列的一個值:
  ODT_BUTTON    自繪按鈕
  ODT_COMBOBOX  自繪組合框
  ODT_LISTBOX  自繪列表框
  ODT_LISTVIEW 自繪列表檢視控制元件
  ODT_MENU 自繪選單
CtlID:
 指定組合框(combo box), 列表框(list box), 或 控鈕(button)的識別符號.這個成員不能在選單中使用

itemID:
 指定選單項的識別符號或組合框(combo box), 列表框(list box)的位置索引.
  列表框(list box)風格已經有LBS_OWNERDRAWVARIABLE時這個值才被指定
  組合框(combo box)風格已經有CBS_OWNERDRAWVARIABLE風格時這個值才被指定

itemWidth:
 指定寬,單位象素,一個選單專案.在從訊息返回之前,自繪選單項的所有者必需填充這個成員.

itemHeight:
 指定高,單位象素,列表框(list box)一個個別的項或一個選單.在從訊息返回之前自繪組合框,列表框或選單項必需填寫這個引數.

itemData:
 指定與應用程式定義的選單項相關聯的32位值.做為控制元件,這個引數指定值是最後指定給列表框(list box)或組合框(combo box)的LB_SETITEMDATA或CB_SETITEMDATA訊息中的值.如果列表框(list box)或組合框(combo box)已經使用LB_HASSTRINGS或CB_HASSTRINGS風格這個最初值是零.否則,這個值最初的值是傳給列表框(list box)或組合框(combo box)
 下列訊息中lparam引數的一個值:
 CB_ADDSTRING
 CB_INSERTSTRING
 LB_ADDSTRING
 LB_INSERTSTRING
備註:
 所有者窗體接收一個自繪控制元件的WM_MEASUREITEM訊息lParam引數指向的MEASUREITEMSTRUCT結構體的指標.就在控制元件被建立的時候自繪控制元件傳送這個訊息到它的所有者窗體.所有者然後為控制元件和返回的結構體填寫適當成員.
 這個結構體是共同的所有自繪控制元件
 如果應用程式沒有填充MEASUREITEMSTRUCT適當的成員.這個控制元件或選單不可能被完全的繪製

==================================================================

WM_MEASUREITEM訊息

WM_MEASUREITEM
在控制元件或選單被建立的時候,向自繪按鈕(button),組合框(combo box),列表框(list box),
列表檢視(list view)或選單項的所有者傳送WM_MEASUREITEM訊息

WM_MEASUREITEM
idCtl = (UINT) wParam;                // 控制元件識別符號
lpmis = (LPMEASUREITEMSTRUCT) lParam; // 專案大小資訊

引數:
idCtl
    wParam值. lpmis引數指向MEASUREITEMSTRUCT結構體CtlID成員包含的值,
 這個值由傳送到控制元件的WM_MEASUREITEM訊息確定

 如果這個值是零.訊息是由一個選單發出.如果這個值是非零,訊息是由組合框(combo box),列表框(list box)
  發出,如果這個值是非零並且lpmis指向的MEASUREITEMSTRUCT結構體itemID成員的值是(UINT)-1,
 訊息是傳送到組合框編輯區域.
lpmis
    lparam值.指向包含自繪控制元件或選單項的尺寸的MEASUREITEMSTRUCT結構體
返回值:
 如果應用程式處理這個訊息,返回的是TRUE

備註:
 就在所有者接收WM_MEASUREITEM訊息的時候,所有者填充訊息中lParam引數指向的MEASUREITEMSTRUCT結構體,
 這告訴系統控制元件的尺寸.如果一個組合框(combo box)或列表框(list box)建立時有
 LBS_OWNERDRAWVARIABLE或 CBS_OWNERDRAWVARIABLE風格,這個訊息傳送到所有者控制元件各自的項;否則,這個訊息傳送一次.

 在WM_INITDIALOG訊息傳送之前,系統為在建立時有OWNERDRAWFIXED風格的列表框和組合框的所有者窗體
 傳送WM_MEASUREITEM訊息.在所有者收到這個訊息的時候,系統還沒有決定控制元件使用字型的寬度和高度尺寸.
 將在應用程式或庫中呼叫主要的函式計算需要的值