?? wtl for mfc programmers, part v.mht
字號:
MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)
MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)
END_MSG_MAP()
};</PRE>
<P>Notice that the main section of the map handles the =
<CODE>WM_*</CODE>=20
messages, while the ALT section handles the reflected versions,=20
<CODE>OCM_*</CODE>. The owner draw notifications are like=20
<CODE>WM_NOTIFY</CODE>, in that you can handle them in the =
control's=20
parent, or reflect them back to the control. If you choose the =
former, you=20
chain messages directly to <CODE>COwnerDraw</CODE>:</P><PRE><SPAN =
class=3Dcpp-keyword>class</SPAN> CSomeDlg : <SPAN =
class=3Dcpp-keyword>public</SPAN> COwnerDraw<CSomeDlg>, ...
{
BEGIN_MSG_MAP(CSomeDlg)
<SPAN class=3Dcpp-comment>//...</SPAN>
CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>)
END_MSG_MAP()
=20
<SPAN class=3Dcpp-keyword>void</SPAN> DrawItem ( LPDRAWITEMSTRUCT =
lpdis );
};</PRE>
<P>However, if you want the control to handle the messages, you =
need to=20
chain to the <CODE>ALT_MSG_MAP(<SPAN =
class=3Dcpp-literal>1</SPAN>)</CODE>=20
section, using the <CODE>CHAIN_MSG_MAP_ALT</CODE> =
macro:</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CSomeButtonImpl : =
<SPAN class=3Dcpp-keyword>public</SPAN> =
COwnerDraw<CSomeButtonImpl>, ...
{
BEGIN_MSG_MAP(CSomeButtonImpl)
<SPAN class=3Dcpp-comment>//...</SPAN>
CHAIN_MSG_MAP_ALT(COwnerDraw<CSomeButtonImpl>, <SPAN =
class=3Dcpp-literal>1</SPAN>)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
=20
<SPAN class=3Dcpp-keyword>void</SPAN> DrawItem ( LPDRAWITEMSTRUCT =
lpdis );
};</PRE>
<P><CODE>COwnerDraw</CODE> unpacks the parameters sent with the =
message,=20
then calls an implementation function in your class. In the =
example above,=20
the classes implement <CODE>DrawItem()</CODE>, which is called =
when=20
<CODE>WM_DRAWITEM</CODE> or <CODE>OCM_DRAWITEM</CODE> is chained =
to=20
<CODE>COwnerDraw</CODE>. The methods you can override =
are:</P><PRE><SPAN class=3Dcpp-keyword>void</SPAN> =
DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
<SPAN class=3Dcpp-keyword>void</SPAN> MeasureItem(LPMEASUREITEMSTRUCT =
lpMeasureItemStruct);
<SPAN class=3Dcpp-keyword>int</SPAN> CompareItem(LPCOMPAREITEMSTRUCT =
lpCompareItemStruct);
<SPAN class=3Dcpp-keyword>void</SPAN> DeleteItem(LPDELETEITEMSTRUCT =
lpDeleteItemStruct);</PRE>
<P>If for some reason you don't want to handle a message, you can =
call=20
<CODE>SetMsgHandled(<SPAN class=3Dcpp-keyword>false</SPAN>)</CODE> =
and the=20
message will be passed along to any other handlers that might be =
later in=20
the message map. This <CODE>SetMsgHandled()</CODE> function is =
actually a=20
member of <CODE>COwnerDraw</CODE>, but it works the same as the=20
<CODE>SetMsgHandled()</CODE> you use with=20
<CODE>BEGIN_MSG_MAP_EX()</CODE>.</P>
<P>For ControlMania2, we'll start with the tree control from=20
ControlMania1, and add an owner-drawn button and handle the =
reflected=20
<CODE>WM_DRAWITEM</CODE> in the button class. Here's the new =
button in the=20
resource editor:</P>
<P><IMG height=3D346 alt=3D" [Owner-drawn button 1 - 7K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC5/cm2_od1.png" =
width=3D325=20
align=3Dbottom border=3D0></P>
<P>Now we need a class that implements this button:</P><PRE><SPAN =
class=3Dcpp-keyword>class</SPAN> CODButtonImpl : <SPAN =
class=3Dcpp-keyword>public</SPAN> CWindowImpl<CODButtonImpl, =
CButton>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
COwnerDraw<CODButtonImpl>
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
BEGIN_MSG_MAP_EX(CODButtonImpl)
CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, <SPAN =
class=3Dcpp-literal>1</SPAN>)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
=20
<SPAN class=3Dcpp-keyword>void</SPAN> DrawItem ( LPDRAWITEMSTRUCT =
lpdis );
};</PRE>
<P><CODE>DrawItem()</CODE> uses GDI calls like =
<CODE>BitBlt()</CODE> to=20
draw a picture on the button face. This code should be easy to =
follow,=20
since once again the WTL class names and methods are similar to =
MFC.</P><PRE><SPAN class=3Dcpp-keyword>void</SPAN> =
CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis )
{
<SPAN class=3Dcpp-comment>// NOTE: m_bmp is a CBitmap init'ed in the =
constructor.</SPAN>
CDCHandle dc =3D lpdis->hDC;
CDC dcMem;
=20
dcMem.CreateCompatibleDC ( dc );
dc.SaveDC();
dcMem.SaveDC();
=20
<SPAN class=3Dcpp-comment>// Draw the button's background, red if it =
has the focus, blue if not.</SPAN>
<SPAN class=3Dcpp-keyword>if</SPAN> ( lpdis->itemState & =
ODS_FOCUS )=20
dc.FillSolidRect ( &lpdis->rcItem, RGB(<SPAN =
class=3Dcpp-literal>255</SPAN>,<SPAN class=3Dcpp-literal>0</SPAN>,<SPAN =
class=3Dcpp-literal>0</SPAN>) );
<SPAN class=3Dcpp-keyword>else</SPAN>
dc.FillSolidRect ( &lpdis->rcItem, RGB(<SPAN =
class=3Dcpp-literal>0</SPAN>,<SPAN class=3Dcpp-literal>0</SPAN>,<SPAN =
class=3Dcpp-literal>255</SPAN>) );
=20
<SPAN class=3Dcpp-comment>// Draw the bitmap in the top-left, or =
offset by 1 pixel if the button</SPAN>
<SPAN class=3Dcpp-comment>// is clicked.</SPAN>
dcMem.SelectBitmap ( m_bmp );
=20
<SPAN class=3Dcpp-keyword>if</SPAN> ( lpdis->itemState & =
ODS_SELECTED )=20
dc.BitBlt ( <SPAN class=3Dcpp-literal>1</SPAN>, <SPAN =
class=3Dcpp-literal>1</SPAN>, <SPAN class=3Dcpp-literal>80</SPAN>, <SPAN =
class=3Dcpp-literal>80</SPAN>, dcMem, <SPAN =
class=3Dcpp-literal>0</SPAN>, <SPAN class=3Dcpp-literal>0</SPAN>, =
SRCCOPY );
<SPAN class=3Dcpp-keyword>else</SPAN>
dc.BitBlt ( <SPAN class=3Dcpp-literal>0</SPAN>, <SPAN =
class=3Dcpp-literal>0</SPAN>, <SPAN class=3Dcpp-literal>80</SPAN>, <SPAN =
class=3Dcpp-literal>80</SPAN>, dcMem, <SPAN =
class=3Dcpp-literal>0</SPAN>, <SPAN class=3Dcpp-literal>0</SPAN>, =
SRCCOPY );
=20
dcMem.RestoreDC(-<SPAN class=3Dcpp-literal>1</SPAN>);
dc.RestoreDC(-<SPAN class=3Dcpp-literal>1</SPAN>);
}</PRE>
<P>Here's what the button looks like:</P>
<P><IMG height=3D324 alt=3D" [Owner-drawn button - 11K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC5/cm2_od2.png" =
width=3D305=20
align=3Dbottom border=3D0></P>
<H3><A name=3Dccustomdraw></A>CCustomDraw</H3>
<P><CODE>CCustomDraw</CODE> works similarly to =
<CODE>COwnerDraw</CODE> in=20
the way you handle <CODE>NM_CUSTOMDRAW</CODE> messages and chain =
them.=20
<CODE>CCustomDraw</CODE> has an overridable method for each of the =
custom=20
draw stages:</P><PRE>DWORD OnPrePaint(<SPAN =
class=3Dcpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnPostPaint(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
DWORD OnPreErase(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
DWORD OnPostErase(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
=20
DWORD OnItemPrePaint(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPostPaint(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPreErase(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPostEraset(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);
=20
DWORD OnSubItemPrePaint(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl, =
LPNMCUSTOMDRAW lpNMCD);</PRE>
<P>The default handlers all return <CODE>CDRF_DODEFAULT</CODE>, so =
you=20
only need to override a method if you need to do your own drawing =
or=20
return a different value.</P>
<P>You might have noticed that in the last screen shot, "Dawn" was =
shown=20
in green. That is achieved by <CODE>CBuffyTreeCtrl</CODE> chaining =
messages to <CODE>CCustomDraw</CODE> and overriding=20
<CODE>OnPrePaint()</CODE> and <CODE>OnItemPrePaint()</CODE>. When =
the tree=20
is filled, that node's item data set to 1, and=20
<CODE>OnItemPrePaint()</CODE> checks for that value and changes =
the text=20
color if it's found.</P><PRE>DWORD =
CBuffyTreeCtrl::OnPrePaint(<SPAN class=3Dcpp-keyword>int</SPAN> idCtrl,=20
LPNMCUSTOMDRAW lpNMCD)
{
<SPAN class=3Dcpp-keyword>return</SPAN> CDRF_NOTIFYITEMDRAW;
}
=20
DWORD CBuffyTreeCtrl::OnItemPrePaint(<SPAN =
class=3Dcpp-keyword>int</SPAN> idCtrl,=20
LPNMCUSTOMDRAW lpNMCD)
{
<SPAN class=3Dcpp-keyword>if</SPAN> ( <SPAN =
class=3Dcpp-literal>1</SPAN> =3D=3D lpNMCD->lItemlParam )
pnmtv->clrText =3D RGB(<SPAN =
class=3Dcpp-literal>0</SPAN>,<SPAN class=3Dcpp-literal>128</SPAN>,<SPAN =
class=3Dcpp-literal>0</SPAN>);
=20
<SPAN class=3Dcpp-keyword>return</SPAN> CDRF_DODEFAULT;
}</PRE>
<P><CODE>CCustomDraw</CODE> also has its own =
<CODE>SetMsgHandled()</CODE>=20
function that's available to your overrides, just as in=20
<CODE>COwnerDraw</CODE>.</P>
<H2><A name=3Dnewwtlctrls></A>New WTL Controls</H2>
<P>WTL has a few new controls of its own that either improve on =
other=20
wrappers (like <CODE>CTreeViewCtrlEx</CODE>), or provide new =
functionality=20
that isn't in the built-in controls (like =
<CODE>CHyperLink</CODE>).</P>
<H3><A name=3Dnewcbitmapbutton></A>CBitmapButton</H3>
<P>WTL's <CODE>CBitmapButton</CODE>, declared in atlctrlx.h, is =
rather=20
easier to use than the MFC version. The WTL class uses an image =
list=20
instead of four separate bitmap resources, which means you can =
keep=20
multiple button images in one bitmap and reduce your GDI usage a =
bit. This=20
is especially nice if you have a lot of graphics and your app runs =
on=20
Windows 9x, because using lots of separate graphics can quickly =
exhaust=20
GDI resources and bring down the system.</P>
<P><CODE>CBitmapButton</CODE> is a =
<CODE>CWindowImpl</CODE>-derived class=20
that has many features including: automatic sizing of the control, =
automatic generation of a 3D border, hot-tracking support, and =
several=20
images per button for the various states the control can be =
in.</P>
<P>In ControlMania2, we'll use a <CODE>CBitmapButton</CODE> along =
side the=20
owner-draw button we created earlier. We start by adding a=20
<CODE>CBitmapButton</CODE> member called <CODE>m_wndBmpBtn</CODE> =
to=20
<CODE>CMainDlg</CODE>. We then connect it to the new button in the =
usual=20
way, either by calling <CODE>SubclassWindow()</CODE> or using DDX. =
We load=20
a bitmap into an image list, then tell the button to use that =
image list.=20
We also tell the button which image in the image list corresponds =
to which=20
control state. Here's the section from <CODE>OnInitDialog()</CODE> =
that=20
sets up the button:</P><PRE> <SPAN class=3Dcpp-comment>// Set =
up the bitmap button</SPAN>
CImageList iml;
=20
iml.CreateFromImage ( IDB_ALYSON_IMGLIST, <SPAN =
class=3Dcpp-literal>81</SPAN>, <SPAN class=3Dcpp-literal>1</SPAN>, =
CLR_NONE,
IMAGE_BITMAP, LR_CREATEDIBSECTION );
=20
m_wndBmpBtn.SubclassWindow ( GetDlgItem(IDC_ALYSON_BMPBTN) );
m_wndBmpBtn.SetToolTipText ( _T(<SPAN =
class=3Dcpp-string>"Alyson"</SPAN>) );
m_wndBmpBtn.SetImageList ( iml );
m_wndBmpBtn.SetImages ( <SPAN class=3Dcpp-literal>0</SPAN>, <SPAN =
class=3Dcpp-literal>1</SPAN>, <SPAN class=3Dcpp-literal>2</SPAN>, <SPAN =
class=3Dcpp-literal>3</SPAN> );</PRE>
<P>By default, the button assumes ownership of the image list, so=20
<CODE>OnInitDialog()</CODE> must not delete the image list it =
creates.=20
Here is the new button in its default state. Notice how the =
control is=20
resized to exactly fit the size of the image.</P>
<P><IMG height=3D324 alt=3D" [WTL bitmap button - 12K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC5/cm2_bb1.png" =
width=3D305=20
align=3Dbottom border=3D0></P>
<P>Since <CODE>CBitmapButton</CODE> is a very useful class, I'll =
cover its=20
public methods here.</P>
<H4>CBitmapButton methods</H4>
<P>The class <CODE>CBitmapButtonImpl</CODE> contains all the code =
to=20
implement a button, but unless you need to override a method or =
message=20
handler, you can use <CODE>CBitmapButton</CODE> for your =
controls.</P>
<H5>CBitmapButtonImpl constructor</H5><PRE>CBitmapButtonImpl(DWORD =
dwExtendedStyle =3D BMPBTN_AUTOSIZE,
HIMAGELIST hImageList =3D NULL)</PRE>
<P>The constructor sets up the button extended style (not to be =
confused=20
with its window styles) and can assign an image list. Usually the =
defaults=20
are sufficient, since you can set both attributes with other =
methods.</P>
<H5>SubclassWindow()</H5><PRE>BOOL SubclassWindow(HWND hWnd)</PRE>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -