; o7 x2 W2 N9 w4 mBOOL CUi4Dlg::OnEraseBkgnd(CDC* pDC) ; A' |1 H: R; t* v{" X& g, J* H7 e
CDC dc;7 r( B- F9 n' T( ~/ i* `6 E2 v
dc.CreateCompatibleDC(pDC); ! t, L1 E$ x9 B& P dc.SelectObject(&m_Back); , g" u5 a" `% s+ {" C/ M 1 z" W8 y" m! N* o+ P //获取BITMAP对象 % ]- f/ `+ y) S6 Z+ @% ] BITMAP hb; / o6 B1 o. }2 W) O/ h, V m_Back.GetBitmap(&hb); 4 g& y+ B6 z- d; m% M( o( M2 O5 I. g( J$ V/ m0 w# S+ o$ m
//获取窗口大小& S- \* K; y: h/ z
CRect rt;* h8 O* v% e# k4 h( v- C
GetClientRect(&rt);8 A3 M5 q$ n: g; y- ^
//显示位图 0 o4 m" I- Z- M& N8 R% c pDC->StretchBlt(0, 0, rt.Width(), rt.Height(),. I' Q1 B3 N2 M# m" j9 ]
&dc, 0, 0, hb.bmWidth, hb.bmHeight, SRCCOPY);/ X3 a6 Q P6 u1 z
+ c# J9 |" s; {8 u
return TRUE;7 O1 M* c; [' N8 _0 ?: S3 \
}: A9 X! i. i+ ^5 T, E! b
: g8 a t0 v l* { i! X" s0 D7 n7 {
HBRUSH CUi4Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) / s3 }, K# T8 {$ L' V; `( `/ j
{# |2 R7 g" d4 d! E/ N/ @- J! N; V" O
//设置透明背景模式 4 _+ i+ z5 {, q* L* {8 g pDC->SetBkMode(TRANSPARENT); & K) i9 y: v) L6 f; C- r //设置背景刷子为空# ~* q: I3 C5 m6 I( w
return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);+ U# q0 k& R' |
}! {7 n9 M0 J- e) s+ d/ Y% u% e
</TEXTAREA> ) r% V( I6 Y. S8 c% H# i; |- E' x
<P>同时别忘了响应OnCtlColor,否则窗口里面的控件就不透明了。OnCtlColor的内容,详见3.3.3章节。 1 a4 { b7 \9 z: w4 m
<P> 6 ~+ \: W; G S8 n4 r# D! f X8 y' l<P>$ o7 y4 @7 T+ `" N/ S4 L J
<P><b>3.3.3 WM_CTLCOLOR </b> 2 {2 }2 ~6 o! g! t0 A- V) K<P><b></b> ( G, x R9 W6 J Q<P>在控件显示之前,每一个控件都会向父对话框发送一个WM_CTLCOLOR消息要求获取绘制所需要的颜色。WM_CTLCOLOR消息缺省处理函数CWnd::OnCtlColor返回一个HBRUSH类型的句柄,这样,就可以设置前景和背景文本颜色,并为控件或者对话框的非文本区域选定一个刷子。 . _2 U. B4 \3 F
<P>WM_CTLCOLOR的映射函数原型如下: ( ~5 U! B4 G0 M$ @4 u# Q
<P>afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor );</P>2 k* B* y; K7 ]0 u
<P>返回值: ( v+ N3 ?. t- P- `" ~' T<P>用以指定背景的刷子 + t Z J" h# H! I3 Z
<P>参数: ' i% X, {2 w& c( Q& \
<P>pDC指定了绘制操作所使用的设备环境。 . H9 U2 G( A+ j9 E
<P>pWnd 控件指针 2 W7 }( v$ b* T/ p7 p' R* V) t: d/ N/ C
<P>nCtlColor 指定控件类型,其取值如表2所示:</P>: J' I' }' @. ?. M: R7 E {
<P>类型值 含义 . @; R$ `: O+ i# a
<P>CTLCOLOR_BTN 按钮控件 , r( q4 `, v. W) q; k2 L# M+ t! z) a: g<P>CTLCOLOR_DLG 对话框 {: {% C- d0 T& t, j<P>CTLCOLOR_EDIT 编辑控件 , J1 C1 y! C" v& W
<P>CTLCOLOR_LISTBOX 列表框 6 V% u* x$ |. p( J5 @( ^* C<P>CTLCOLOR_MSGBOX 消息框 C6 i: U4 k3 F$ P+ E0 g8 q# t<P>CTLCOLOR_SCROLLBAR 滚动条 " O& D5 }( b7 N; m<P>CTLCOLOR_STATIC 静态控件 / H+ n4 N& u1 K+ c6 N5 [* B<P>表2 nCtlColor的类型值与含义</P>8 G0 q1 B$ h: L; k4 Y
<P>作为一个简单的例子,观察以下的代码: ! T J6 x5 ~5 f7 z+ {) ?5 ]
<P><TEXTAREA readOnly>BOOL CUi5Dlg::OnInitDialog()7 ^9 a+ }2 @. P
{; ~. z& s9 C, F8 t) \/ A
//… " ?( \1 a9 Q. D( _+ O) X //创建字体+ o- ~+ R0 J1 b" h6 O
//CFont CUi1View::m_Font1, CUi1View::m_Font2; Z2 ^7 t% \5 v$ U
m_Font1.CreatePointFont(120, "Impact");, l$ }$ V$ g g1 N9 ]
m_Font3.CreatePointFont(120, "Arial"); . c# n k# A) R & J+ c. p" ?7 |( f/ O return TRUE; // return TRUE unless you set the focus to a control ' @8 h. U4 l m( a' [
} 7 i7 l; f. X: b5 h3 j+ z8 D 6 k, U" D- B) N. C' U0 e" m4 OHBRUSH CUi5Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) * [; P- A& _' X- f
{ . w* ~; k7 a% a2 R9 s, c HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);/ i% _' ~& U" F- b( A
if(nCtlColor == CTLCOLOR_STATIC) 1 O/ j" @2 f2 O( V; a8 c {+ B& D$ O2 Q* J& F7 d* f, M
//区分静态控件 ; U9 d! N' T3 |! X: I1 Z: P switch(pWnd->GetDlgCtrlID()) 5 k1 i: Y2 R5 o! B3 V) H8 C) G { / j- I ~( z. A4 z3 s. T0 D case IDC_STATIC1: ; Z+ @. [, x8 d7 ?5 t5 t" @6 _, W {0 J, ~& t2 C7 C" q$ n3 m
pDC->SelectObject(&m_Font1); ' j( ?- o! F0 N9 J% F. D& Q6 v/ F pDC->SetTextColor(RGB(0, 0, 255));" v v/ e3 t$ p% C) z) J
break; + M, w- F# Z4 l: ]+ M, d' {" C } / ~( S/ x- Z% ~$ C; O case IDC_STATIC2:, }( ^5 W) E- O# Y+ ]6 f& R! C- L2 C3 \
{ ( I- u ?# M z5 O. L pDC->SelectObject(&m_Font2); 5 E6 Z* ]7 Q$ I pDC->SetTextColor(RGB(255, 0, 0));0 R+ g G* C( W* Q8 u
break; 6 Y$ h# Q* m' O$ V: V7 e% [ }5 ]+ S1 O3 X$ s& `, z5 a, p
} : i/ I% u) H8 l; ]9 s } 9 `4 J# S$ Y! l! @- k9 b$ Q / n A: ?9 j" ?- ?; D( y r1 I return hbr; / f O$ W& W X8 }/ d( |, I$ C |}6 X, G' h1 K$ V' q j) |5 v: Z# \
</TEXTAREA> 5 ~5 ]7 E1 L( \4 L; ~- a6 s
<P>生成的界面如下: $ p2 B$ Z" u$ \; M5 p( [" d<P align=center><IMG src="http://vcer.net/upload/2004/03/1046650321578.gif" border=0></P> 6 Y1 Z8 e3 k2 u<P align=center> 图7 利用WM_CTLCOLOR消息美化界面 </P>2 A) m( E, D( M( {; r# o
<P><b>3.3.4 WM_DRAWITEM </b> # M- _( W' Q" h# B<P><b></b> 3 R5 n+ `7 i) E4 u) G3 D A
<P>OnCtlColor只能修改元素的颜色,但不能修改元素的界面框架,WM_DRAWITEM则可以。 - G+ W: K2 ^8 m) ^: K<P>当一个具有Owner draw风格的元素(包括按钮、组合框、列表框和菜单等)需要显示外观时,该元素会发送一条WM_DRAWITEM消息至它的隶属窗口(Owner)。 " C2 ^ g" m+ |# X6 q* A
<P>WM_DRAWITEM的映射函数原型如下: , S9 E: U4 J1 a) Z( u7 `1 z# ~<P>afx_msg void OnDrawItem( int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct );</P>/ x/ S/ ? V3 D( F$ v
<P>参数: ; e% K% b( F3 @7 q% L3 \1 E
<P>nIDCtl 该控件的ID,如果该元素为菜单,则nIDCtl为0 $ c6 o8 C: M3 m% k' Q* ^
<P>lpDrawItemStruct 指向DRAWITEMSTRUCT结构对象的指针,DRAWITEMSTRUCT的结构定义如下: : \$ _# M( d4 c' e+ t$ f& l. w
<P><TEXTAREA readOnly>typedef struct tagDRAWITEMSTRUCT 0 G( Z( G+ L: l% \" G{ / R$ \1 ]7 O) E( U* } UINT CtlType; 9 `2 Q, h+ R6 B
UINT CtlID; 0 K; i, C6 V) b8 z6 {* ?, v8 |
UINT itemID; * z3 d4 b! ?# G1 R x% h UINT itemAction;0 F) _- c0 K2 d2 I( Q6 g
UINT itemState;; j2 `* E5 F; k$ P( e+ t
HWND hwndItem; ) O3 l8 V0 C: r- ~6 | HDC hDC; % M; o: {9 ?. v* U& D, S. C6 Z% y, y RECT rcItem; V: n6 M+ x4 @& n5 S- v w* Q DWORD itemData; 2 s+ Z; \# k* R}DRAWITEMSTRUCT; * r, z+ g& |4 q) `5 G</TEXTAREA> 5 V) k' o4 G, _0 Q0 e
<P>CtlType指定了控件的类型,其取值如表3所示: / \0 m+ K }! _2 N9 v
<P>类型值 含义 9 L9 t: @ Q9 ]6 v( H5 j# v/ P6 I+ v<P>ODT_BUTTON 按钮控件 $ R! X# |% T: v<P>ODT_COMBOBOX 组合框控件 - C) D5 ]$ w9 ~6 G+ m. O
<P>ODT_LISTBOX 列表框控件 7 o8 S: Q' Q I/ d+ {/ p<P>ODT_LISTVIEW 列表视图 % t5 w, R8 P9 f4 G4 d<P>ODT_MENU 菜单项 / F, O1 O# s, r
<P>ODT_STATIC 静态文本控件 , X( K# N! a$ r5 k0 F
<P>ODT_TAB Tab控件 5 O. v4 S/ F& j H3 N9 R. q1 s, \<P>表3 CtlType的类型值与含义</P> - i% X1 z8 q' s' \* S# X) O<P>CtlID 指定自绘控件的ID值,该成员不适用于菜单项 0 _) n0 ^$ l! k" P+ A9 |<P>itemID表示菜单项ID,也可以表示列表框或者组合框中某项的索引值。对于一个空的列表框或组合框,该成员的值为?C1。这时应用程序只绘制焦点矩形(该矩形的坐标由rcItem 成员给出)虽然此时控件中没有需要显示的项,但是绘制焦点矩形还是很有必要的,因为这样做能够提示用户该控件是否具有输入焦点。当然也可以设置itemAction 成员为合适值,使得无需绘制焦点。 6 } B2 n2 w$ W4 w6 j$ l<P>itemAction 指定绘制行为,其取值为表4中所示值的一个或者多个的联合:</P>, K, R- Q- `5 C; s& x9 d6 W4 E
<P>类型值 含义 5 G6 I: u, j$ N4 h5 t) W1 I! w/ P
<P>ODA_DRAWENTIRE 当整个控件都需要被绘制时,设置该值。 ' N* a& ?! E$ P) L
<P>ODA_FOCUS 如果控件需要在获得或失去焦点时被绘制,则设置该值。此时应该检查itemState成员,以确定控件是否具有输入焦点。 & W! a- i9 f) t) }8 x4 F<P>ODA_SELECT 如果控件需要在选中状态改变时被绘制,则设置该值。此时应该检查itemState 成员,以确定控件是否处于选中状态。 0 j: t6 ?& f% `8 g. A3 Z# o: ~5 T
<P>表4 itemAction的类型值与含义</P>! q! J. b* q* I% b$ H
<P>itemState 指定了当前绘制项的状态。例如,如果菜单项应该被灰色显示,则可以指定ODS_GRAYED状态标志。其取值为表5中所示值的一个或者多个的联合:</P>- ~3 g; ^0 J% ?7 P3 L
<P>类型值 含义 ! W5 I8 O6 e" h4 a! `. `
<P>ODS_CHECKED 标记状态,仅适用于菜单项。 . _! m! l# O" X; q# C$ Z$ v# I<P>ODS_DEFAULT 默认状态。 $ ~4 x2 P- ~4 K
<P>ODS_DISABLED 禁止状态。 7 W- z( s2 ]1 q& y' j2 ?<P>ODS_FOCUS 焦点状态。 6 b0 m* B6 s) i$ j9 s9 Q1 i$ s$ {<P>ODS_GRAYED 灰化状态,仅适用于菜单项。 ' E" }; g% {& ?$ l' M$ h<P>ODS_SELECTED 选中状态。 & i4 \/ r' H6 {: A; {9 G6 D<P>ODS_HOTLIGHT 仅适用于Windows 98/Me/Windows 2000/XP,热点状态:如果鼠标指针位于控件之上,则设置该值,这时控件会显示高亮颜色。 % F6 R9 W$ @1 b
<P>ODS_INACTIVE 仅适用于Windows 98/Me/Windows 2000/XP,非激活状态。 5 {, C) O: u# T$ N/ o
<P>ODS_NOACCEL 仅适用于Windows 2000/XP,控件是否有快速键。 . O' F: ?6 c7 c! `2 M8 h( s- K<P>ODS_COMBOBOXEDIT 在自绘组合框控件中只绘制选择区域。 1 ?; o& }7 t( g/ d' _
<P>ODS_NOFOCUSRECT 仅适用于Windows 2000/XP,不绘制捕获焦点的效果。 * l- a+ N4 P% h
<P>表5 itemState的类型值与含义</P>- @8 X/ U2 _3 M, L; h) F. C
<P>hwndItem 指定了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象为菜单项,则表示包含该菜单项的菜单句柄。 / ^% }: U" g* D; W<P>hDC 指定了绘制操作所使用的设备环境。 : i$ t$ |$ a8 I) I<P>rcItem 指定了将被绘制的矩形区域。这个矩形区域就是上面hDC的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自绘制区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。 $ F7 m# {$ G$ m: a& p/ y# \<P>itemData i' q) R" w+ W. y. X1 q8 \
<P>对于菜单项,该成员的取值为由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等函数传递给菜单的值。 : g: J3 ?5 D- L1 n
<P>对于列表框或这组合框,该成员的取值为由ComboBox::AddString、CComboBox::InsertString、CListBox::AddString或者CListBox::InsertString等函数传递给控件的值。 E1 T7 B, ?1 Y
<P>如果ctlType 的取值是ODT_BUTTON或者ODT_STATIC,itemData的取值为0。 $ s8 \+ [, b. x s8 P& [" A/ J<P>图5是个相应的例子,它修改了按钮的界面: % W: d. w; e5 P7 n4 C
<P align=center><IMG src="http://vcer.net/upload/2004/03/1046650324712.gif" border=0></P>6 Z. v; z3 m9 S4 {! F
<P> # W5 C, K5 Q: b/ U<P align=center>图8 利用WM_DRAWITEM消息美化界面</P>* w$ O1 C. w7 R/ j0 g% b( n
<P>实现代码如下: : G* i! |7 n2 o7 K" `8 G9 F/ Y
<P><TEXTAREA readOnly>BOOL CUi6Dlg::OnInitDialog() ' g: T/ l8 H1 s @8 S{3 k; Q# ]: H* B
//…7 d: l! h+ V: {% d+ R+ n' P* ^
//创建字体 ! Q5 F8 Q8 `9 j$ ^- {1 r //CFont CUi1View::m_Font' S3 E5 _: _- `
m_Font.CreatePointFont(120, "Impact");) U B. s9 X0 i+ z0 [
//… * o" H' z3 _0 q5 z" {} 7 X* t# _- s5 R! F4 e# U- J6 n" H' m0 k+ ?% d. F( x# k- l- t0 }
void CUi6Dlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) ( O4 `3 H1 C7 X* C% C6 }; L; f
{ 1 |2 _4 a* q8 z& N1 W3 v' _ if(nIDCtl == IDC_HELLO_CFAN) s8 B1 g2 p; Y0 @, h5 F" t
{ 5 J7 n: \6 ^6 p4 ^9 A //绘制按钮框架: ~) q' Z) T) E1 k2 ?8 x/ o
6 m, X8 s. N* n8 n" A
UINT uStyle = DFCS_BUTTONPUSH;' a" z+ n; s S3 l0 t4 C* ~
//是否按下去了? . u0 N1 v4 J6 E3 I if (lpDrawItemStruct->itemState & ODS_SELECTED) 2 o7 @. c# j; ~* ` uStyle |= DFCS_PUSHED; ; g, Z: S; ~6 \: W \5 |# D* F 9 B, ^% S& P4 E& V) t, `2 C f3 {' ~ CDC dc;# k3 \+ n: P# E& X
dc.Attach(lpDrawItemStruct->hDC);% Y! S3 [2 o* t1 V
dc.DrawFrameControl(&lpDrawItemStruct->rcItem, DFC_BUTTON, uStyle);& p6 H+ c3 Z# J' u8 c+ Y) O
. [; k- l @4 B+ w! m! r //输出文字 5 a) S! c! p0 q4 [. T3 J- S dc.SelectObject(&m_Font); 2 F) @7 j+ ~2 o, ^, S dc.SetTextColor(RGB(0, 0, 255));; X( J& @4 Z7 _% L- }; w0 l
dc.SetBkMode(TRANSPARENT);9 ?7 \1 @# O, P0 \8 a6 N
& r* P, q5 O$ V5 E& r& i CString sText; . F6 C8 J& t1 _; L+ j m_HelloCFan.GetWindowText(sText);0 o- f4 j7 ^, Z6 ]2 Y
dc.TextOut(lpDrawItemStruct->rcItem.left + 20, lpDrawItemStruct->rcItem.top + 20, sText); 7 U1 H: y+ ?2 O$ Q) y, X' B# @9 K ; f6 O9 T0 @& u //是否得到焦点+ G: \- q% v; R3 o, b
if(lpDrawItemStruct->itemState & ODS_FOCUS)& P" Z% U( Q+ v" S
{. v& W* g7 O. O
//画虚框# W* N d' b2 p+ ]+ H1 `
CRect rtFocus = lpDrawItemStruct->rcItem; # g5 A# o* V! l* M rtFocus.DeflateRect(3, 3); - p0 `: ]8 i# ~8 K( h dc.DrawFocusRect(&rtFocus); , J" X% U3 B: [ }' x X5 I7 H6 X
0 Q( T7 g" L" I
return;2 y. }6 H q" ?8 A! A
}# I/ t# g7 B ]* {4 U
CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);9 O* |5 b5 r/ A' m$ }2 J: p( W/ f. W
} u% n6 ^% ]7 E5 w2 F0 j/ V</TEXTAREA> ( S. S I" I( V' x* a6 N
<P>别忘了标记Owner draw属性: 0 h$ h: i/ P% w) P' ?) m, ]
<P align=center><IMG src="http://vcer.net/upload/2004/03/1046596492605.gif" border=0></P> 9 `, Y2 Z' w+ {: `" x) K/ g+ W; N<P align=center> 图9 指定按钮的Owner draw属性</P>" Y+ N0 G2 O" x: W/ a. m% @
<P>值得一提的是,CWnd内部截获了WM_DRAWITEM、WM_MEASUREITEM等消息,并映射成子元素的相应虚函数的调用,如CButton:rawItem()。所以,以上例子也可以通过派生出一个CButton的派生类,并重载该类的DrawItem()函数来实现。使用虚函数机制实现界面美化参见3.4章节。 9 ?3 H1 z \: R
<P>; J( g7 ]; m5 p6 }
<P>8 S# a1 T! j/ W, Q. s2 P' Q
<P><b>3.3.5 WM_MEASUREITEM</b> ' }( c) F4 T: S, S<P># Y* _; D+ N# x, V+ ~% I+ U( U
<P> " r4 Y1 k b& x- ~3 z6 R& ]<P>仅仅WM_DRAWITEM还是不够的,对于一些特殊的控件,如ListBox,系统在发送WM_DRAWITEM消息前,还发送WM_MEASUREITEM消息,需要你设置ListBox中每个项目的高度。 9 @4 M/ K4 Y) W0 o! d
<P>WM_DRAWITEM的映射函数原型如下: 6 f; U& |' G/ I, {/ Q
<P>afx_msg void OnMeasureItem( int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct ); , X+ u, V3 j0 N/ Z
<P>nIDCtl 该控件的ID,如果该元素为菜单,则nIDCtl为0 / Z6 d5 r6 G z! d! V<P>lpMeasureItemStruct指向MEASUREITEMSTRUCT结构对象的指针,MEASUREITEMSTRUCT的结构定义如下: " S! h8 m/ H/ R) G* O
<P><TEXTAREA readOnly>typedef struct tagMEASUREITEMSTRUCT 8 J- d# g: M! P) S* _- [{2 o3 j. _0 K$ b0 K
UINT CtlType; 3 r" K Z- B) I% p) f5 ^- a UINT CtlID;* [$ b2 H+ k" r2 N$ U' x+ ]$ H0 {
UINT itemID;, p5 f6 P8 w; {" r1 _+ d
UINT itemWidth;5 Q* A8 _8 C/ }. r2 C6 z+ ~
UINT itemHeight; * G$ o$ M2 P+ a: X1 A+ s* }/ e& o DWORD itemData 9 P. B5 ?+ R* n3 a} MEASUREITEMSTRUCT;/ m, R7 m" @0 _ }0 g3 u" c
</TEXTAREA> ) o# J+ d: L! {$ F- O
<P>CtlType指定了控件的类型,其取值如表6所示: 0 ` e7 h9 P3 T0 K' z: h8 c
<P>类型值 含义 . b2 q5 l4 W8 a7 n<P>ODT_COMBOBOX 组合框控件 ; U T9 N' V0 w& r
<P>ODT_LISTBOX 列表框控件 9 J7 o3 K: t8 c7 i+ D4 V4 {! D3 s<P>ODT_MENU 菜单项 9 l* G6 C Q8 j+ `% F<P>表6 CtlType的类型值与含义</P>. e# @2 j9 m- n! n. F; s
<P>CtlID 指定自绘控件的ID值,该成员不适用于菜单项 . U: C0 Y l9 o1 B) X
<P>itemID表示菜单项ID,也可以表示可变高度的列表框或组合框中某项的索引值。该成员不适用于固定高度的列表框或组合框。 , X* n/ i- y' ~( z! f3 e* q<P>itemWidth 指定菜单项的宽度 ! m( S Q( U8 r# B% A0 g<P>itemHeight指定菜单项或者列表框中某项的的高度,最大值为255 p0 N4 l9 U; T3 `/ [# H<P>itemData : [/ d! n8 y' E6 p% ]<P>对于菜单项,该成员的取值为由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等函数传递给菜单的值。 ' K5 L$ D5 U2 w) ~# e
<P>对于列表框或这组合框,该成员的取值为由ComboBox::AddString、CComboBox::InsertString、CListBox::AddString或者CListBox::InsertString等函数传递给控件的值。 # s; a- k9 ~, t& M<P>图示出了OnMeasureItem的效果: 8 T6 N) \8 E7 d' U3 Q1 v
<P align=center><IMG src="http://vcer.net/upload/2004/03/1046650332513.gif" border=0></P> 4 x) n# ?2 t+ Q5 }* m<P align=center> 图10 利用WM_MEASUREITEM消息美化界面</P> ; Q' R0 p) j7 g# {6 w, t' ?3 `7 w<P>相应的OnMeasureItem()实现如下: ' ^# A: o+ Z6 D) v1 X! e7 X<P><TEXTAREA readOnly>void CUi7Dlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) ) a% |3 j. j7 M; X5 x
{6 U4 ^. v; @- H) u/ b
if(nIDCtl == IDC_COLOR_PICKER) ) a* N+ J# U! u1 U2 Q2 p0 J" C' X {1 k; A2 y9 \( s- _
//设定高度为30' P4 U% U; g9 A, X) `
lpMeasureItemStruct->itemHeight = 30;& L6 ?& j) {. o9 w: ~
return; . {! Z- N. u2 b7 L* s }+ N8 Q4 ]" i" D8 Y3 `8 Z
CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct); * m) r0 \7 ^- g) X}" h2 H$ B0 E0 b9 n( ?9 \$ D3 z
</TEXTAREA> / R# R5 V5 E$ R$ ?1 U
<P>同样别忘了指定列表框的Owner draw属性: % W: Z" ^4 K, Z* H
<P align=center><IMG src="http://vcer.net/upload/2004/03/1046596451727.gif" border=0></P> : [* y: `, S' _ K+ h8 `<P>: h5 A' c& E2 `# O' g" U
<P align=center>图11 指定下拉框的Owner draw属性 ! a! ~/ F0 X( l3 h8 I; I$ |# ]<P align=center> 4 `3 T! ` O- Q1 X* O. X" k
<P><b>3.3.6 NM_CUSTOMDRAW</b> * y: X! I% v( A, m9 V# q9 _2 x<P> ! P4 v5 W4 J3 l<P> 0 U( _: U5 m) C) W<P>大家也许熟悉WM_NOTIFY,控件通过WM_NOTIFY向父窗口发送消息。在WM_NOTIFY消息体中,部分控件会发送NM_CUSTOMDRAW告诉父窗口自己需要绘图。 $ ^1 s2 L5 R9 g, H% F<P>可以反射NM_CUSTOMDRAW消息,如: $ L4 p7 J ~6 W1 K5 z, U<P>ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) $ |1 w+ m: H" | t<P>afx_msg void OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult); ( w: w( f3 J3 m<P>参数: 2 H) O; U7 \3 W/ }<P>pNMHDR 说到底只是一个指针,大多数情况下它指向一个NMHDR结构对象,NMHDR结构如下: 8 @, S. X9 M( ~$ `/ c- B<P><TEXTAREA readOnly>typedef struct tagNMHDR) I. [, b6 i2 q5 _3 m# ^+ Q
{ ( h) ]7 S; t' D& Z* f HWND hwndFrom; / B) R6 m" J( |7 r
UINT idFrom; 5 a; v; `. h: M3 d
UINT code; + U0 l$ X# ^4 O. r7 i} NMHDR; 0 ?% C% u3 l2 F3 j) a</TEXTAREA> 0 T2 _) b6 ~9 M9 U<P>其中: 9 v; t: w! k0 K+ t7 |( f0 ?; X<P>hwndFrom 发送方控件的窗口句柄 * i% A$ L3 s6 _4 ^. Z! Z0 R& Y9 y<P>idFrom 发送方控件的ID / t1 ]+ i% d" I0 u7 B) Y+ W% ~
<P>code 通知代码 # c0 f( S( l6 x+ J A<P>对于某些控件来说,pNMHDR则会解释成其它内容更丰富的结构对象的指针,如:对于列表控件来说,pNMHDR常常指向一个NMCUSTOMDRAW对象,NMCUSTOMDRAW结构如下: 1 d" G9 y% v5 Y# N( d' X
<P><TEXTAREA readOnly>typedef struct tagNMCUSTOMDRAWINFO 1 t z' r% n. ?9 D! B$ K; v{ 5 _, H6 k0 J S/ W" U- q5 r4 { NMHDR hdr; - s- a7 F4 q! l8 N; b- i9 b$ i DWORD dwDrawStage; : `! G6 G* O& l3 o, }4 D' P- a HDC hdc; 0 ~( Y8 A g4 n9 e- w RECT rc;9 U* Z9 U1 H; d& f5 L. y
DWORD dwItemSpec; ) V9 F3 R* ]) I5 o9 a: {5 G UINT uItemState;6 v; S" w! D9 z) n) Y( ^1 q3 S. ^
LPARAM lItemlParam;% l6 c" [5 O* F7 M
} NMCUSTOMDRAW, FAR * LPNMCUSTOMDRAW; 7 W% V) E! v: H6 f4 a- m4 b% ]</TEXTAREA> ! }2 |# f( f( R, X2 Q$ I- j<P>hdr NMHDR对象 6 `* x4 ?. d+ J- B0 C$ N
<P>dwDrawStage 当前绘制状态,其取值如表7所示:</P> 0 t# G" S( K2 z6 o5 N<P>类型值 含义 $ q3 t4 c5 ^1 F8 c<P>CDDS_POSTERASE 擦除循环结束 ' \0 o) m; n2 [% a2 D' B h
<P>CDDS_POSTPAINT 绘制循环结束 7 I& u. ^& l( \7 l6 w<P>CDDS_PREERASE 准备开始擦除循环 0 ^: m, O7 l/ ^3 K<P>CDDS_PREPAINT 准备开始绘制循环 " C/ ?# B* D% x$ s0 i<P>CDDS_ITEM 指定dwItemSpec, uItemState, lItemlParam参数有效 % O8 Z/ \1 J! [ q6 j! W8 o<P>CDDS_ITEMPOSTERASE 列表项擦除结束 # D$ F2 C* D% I8 S<P>CDDS_ITEMPOSTPAINT 列表项绘制结束 / \/ {* W9 P. z<P>CDDS_ITEMPREERASE 准备开始列表项擦除 1 i5 h5 @) A9 i( E( S<P>CDDS_ITEMPREPAINT 准备开始列表项绘制 9 O2 d" i* M% z& m, V. _, T: ?* Y<P>CDDS_SUBITEM 指定列表子项</P> # A0 o5 g7 a9 z+ _<P>表7 dwDrawStage的类型值与含义</P> ( z* u! ^2 l; ~, L% k, v<P>hdc指定了绘制操作所使用的设备环境。 7 c* [- z8 ]' c6 ?<P>rc指定了将被绘制的矩形区域。 4 y' I7 }# R5 V; L: E& H
<P>dwItemSpec 列表项的索引 " G) b1 O+ [/ Q; w# `+ _! o
<P>uItemState 当前列表项的状态,其取值如表8所示:</P>" W. u" G' ?2 Y1 w- B9 p
<P>类型值 含义 ) Z4 r8 Z, @# `& j/ Z9 _" Q<P>CDIS_CHECKED 标记状态。 ! M, A1 H) m( u
<P>CDIS_DEFAULT 默认状态。 % n5 ]: K4 Z2 @" a( _
<P>CDIS_DISABLED 禁止状态。 + x- f& \! G/ K% g8 U<P>CDIS_FOCUS 焦点状态。 % p: |, l2 w& g1 U<P>CDIS_GRAYED 灰化状态。 , w6 @# h+ o' B# ?) o5 U8 i( `' B<P>CDIS_SELECTED 选中状态。 - R4 N: G- z* `$ w6 f. ?
<P>CDIS_HOTLIGHT 热点状态。 # s& }: C/ y8 m0 {7 j4 C: q
<P>CDIS_INDETERMINATE 不定状态。 ; z" {2 o% |; J: ~1 v<P>CDIS_MARKED 标注状态。</P> & J3 S. J7 {- x8 {) c, v<P>表8 uItemState的类型值与含义</P> ' L+ x7 ]' [& P<P>lItemlParam 当前列表项的绑定数据 $ B% E! E% _0 v( k<P>pResult 指向状态值的指针,指定系统后续操作,依赖于dwDrawStage: ' {" E( e5 Q$ a$ Y& X0 \8 y9 }9 x
<P>当dwDrawStage为CDDS_PREPAINT,pResult含义如表9所示:</P>2 m) e* `) ~* U" I, I# w; O0 \
<P>类型值 含义 0 K) g% |8 v, s3 o5 X<P>CDRF_DODEFAULT 默认操作,即系统在列表项绘制循环过程不再发送NM_CUSTOMDRAW。 - z9 P; F; j' S9 \7 d) A% w
<P>CDRF_NOTIFYITEMDRAW 指定列表项绘制前后发送消息。 % J2 b# N. F& {3 T) c4 y4 j<P>CDRF_NOTIFYPOSTERASE 列表项擦除结束时发送消息。 3 f6 a, n/ w9 P5 L! ~6 O' Q<P>CDRF_NOTIFYPOSTPAINT 列表项绘制结束时发送消息。</P> 9 n9 @0 o O: J# @4 x<P>表9 pResult的类型值与含义(一) - o* c ^: C* m1 y' t& ]# X<P>当dwDrawStage为CDDS_ITEMPREPAINT,pResult含义如表10所示:</P> $ [' g! i6 @! p. S& X8 e! j<P>类型值 含义 % |9 W- u5 L/ t5 X
<P>CDRF_NEWFONT 指定后续操作采用应用中指定的新字体。 G9 N5 O0 O W
<P>CDRF_NOTIFYSUBITEMDRAW 列表子项绘制时发送消息。 ) J0 w' I9 T5 E6 E3 j7 [& o
<P>CDRF_SKIPDEFAULT 系统不必再绘制该子项。</P>$ [2 K/ ^9 T+ v; O
<P>表10 pResult的类型值与含义(二)</P>5 W6 q: e# m+ p
<P>以下是一个利用NM_CUSTOMDRAW消息绘制出的多色列表框的例子: ( X2 q( `: a- K. W0 V3 F$ O<P align=center><IMG src="http://vcer.net/upload/2004/03/1046650317752.gif" border=0></P>7 @# J" P9 V s6 ` ~* A
<P> & W7 U5 o5 X2 R4 p<P align=center>图12 利用NM_CUSTOMDRAW消息美化界面 5 q; K# A4 y; e; R5 B. b( i# e
<P>对应代码如下: 1 f6 K$ r; m5 I' C( @/ |1 `' {<P><TEXTAREA readOnly>void CCoolList::OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult) ' _7 Q2 r9 c6 h+ C, r& ?{( h1 `4 D6 ]" L, l4 _, H% t( C3 `
//类型安全转换: f% t; m- n4 b2 j+ N
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR); 6 B' h- U4 P ?6 ?! U- N$ N# i+ N% a *pResult = 0;& Q4 [3 H3 \5 V! q
' @7 {# W# F' a
//指定列表项绘制前后发送消息8 M3 k) y5 p# X7 N4 i# \
if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) : a9 _+ C' O, j6 M2 [. a% M. _ { 6 l* a$ A$ a( S' ?! u *pResult = CDRF_NOTIFYITEMDRAW;/ _7 y/ K" W( J( m q; _
} / v4 l$ a5 Y0 Y) M else if(CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) 3 Y0 w- {8 M$ b+ B8 W* A" K8 L { . y: O6 W& v) t1 m: G //奇数行, G7 k a1 s$ S8 N+ e# i; b' Q! `- F
if(pLVCD->nmcd.dwItemSpec % 2) 5 u5 C- Q- J. _3 r2 n" K( M+ \ pLVCD->clrTextBk = RGB(255, 255, 128); % f+ N; F1 t4 ^: ^) ^- C/ J //偶数行) b6 E+ ~1 q+ f h8 t
else, I. \ G, k3 l) e6 z( C
pLVCD->clrTextBk = RGB(128, 255, 255); 4 b- U" }3 @% u //继续 0 N7 \( ^0 c! C; j9 Z *pResult = CDRF_DODEFAULT; n H8 B8 C" t0 f1 c
}6 Q4 K# Y9 f0 W$ H; z% Z* K
}# U+ L2 q; R5 f) i. n, z
</TEXTAREA> & `% B1 O: D ~4 j" _& t; c
<P>注意到上例采取了3.1所推荐的第2种实现方法,派生了一个新类CCoolList。 ; S3 ?3 O; }, c m. k4 S' h5 F<P> $ z6 Q6 c6 `3 d" N1 G<P> * F$ ]/ W9 |. h8 o' _" x3 W+ W" {* V<P><b>3.4 使用MFC类的虚函数机制</b> 0 b# w' Q5 ?1 q) d- `8 m" B<P> 7 g. `) z7 y% O/ Q' a- o4 r# ]5 n" r<P>. ]2 t$ H# A2 X7 T
<P>修改Windows界面,除了从Windows消息机制下功夫,也可以从MFC类下功夫,这应该得益于类的虚函数机制。为了防止诸如“面向对象技术”等术语在此泛滥,以下仅举一段代码作为例子: ) J. b, d' }" p7 g; e5 B( ^
<P><TEXTAREA readOnly>void CView::OnPaint() " `# v$ _8 y- `, \$ R* [8 C{ 3 n* z0 x( L- \# i8 j // standard paint routine, o1 `2 y" [0 p) ^
CPaintDC dc(this);" R" C0 x" R( u( l4 k
OnPrepareDC(&dc);: S; b; _% j2 g' B
OnDraw(&dc);9 l- p! p+ k5 Q4 w+ W
} . \& K8 e5 T. ]: d</TEXTAREA> % g& ~; s6 S9 v M" j<P>这是MFC中viewcore.cpp中的源代码,很多读者总不明白OnDraw()和OnPaint()之间的关系,从以上的代码中很容易看出,CView的WM_PAINT消息响应函数OnPaint()会自动调用CView::OnDraw()。而作为开发者的用户,可以通过简单的OnDraw()的重载实现对WM_PAINT的处理。所以说,对MFC类的虚函数的重载是对消息机制的扩展。 % `9 z: _+ N& H! S<P>以下列出了与界面美化相关的虚函数,参数说明略去: , [$ R. ~$ ^- D% R, ]2 o
<P>CButton:rawItem / ], F' H. f* h
<P>CCheckListBox:rawItem & W7 Z. \0 V% d0 n/ i8 X<P>CComboBox:rawItem 8 `9 o) k% k$ j" q: j
<P>CHeaderCtrl:rawItem ) H5 {$ w& o: l# E
<P>CListBox:rawItem 1 |. `% {/ Y7 `
<P>CMenu:rawItem - \. Q; D' n' w* f$ Q2 ]<P>CStatusBar:rawItem % f4 B& @; Z3 U. o' |6 u$ ]
<P>CStatusBarCtrl:rawItem $ Q _/ H1 R) z" J r6 b% d' y<P>CTabCtrl:rawItem</P> / g- L6 P, T1 J0 m) \ s<P>virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ); * P( q3 E3 P( b7 a7 q: `% C
<P>Owner draw元素自绘函数 + g4 l' Z) X, G* ?3 L- f<P>很显然,位图菜单都是通过这个DrawItem画出来的。限于篇幅,在此不再附以例程。 </P></DIV>