📄 ch21.htm
字号:
{
int width = (di.prcBounds->right - di.prcBounds->left + 1);
int height = (di.prcBounds->bottom - di.prcBounds->top + 1);
BOOL drawn = FALSE;
if (m_nReadyState == READYSTATE_COMPLETE)
{
if (BitmapDataLoaded)
{
hBitmap = ::CreateDIBitmap(di.hdcDraw, &bmih, CBM_INIT, lpvBits,
lpbmi, DIB_RGB_COLORS);
if (hBitmap)
{
HDC hmemdc;
hmemdc = ::CreateCompatibleDC(di.hdcDraw);
::SelectObject(hmemdc, hBitmap);
DIBSECTION ds;
::GetObject(hBitmap,sizeof(DIBSECTION),(LPSTR)&ds);
::StretchBlt(di.hdcDraw,
di.prcBounds->left, // left
di.prcBounds->top, // top
width, // target width
height, // target height
hmemdc, // the image
0, //offset into image -x
0, //offset into image -y
ds.dsBm.bmWidth, // width
ds.dsBm.bmHeight, // height
SRCCOPY); //copy it over
drawn = TRUE;
::DeleteObject(hBitmap);
hBitmap = NULL;
::DeleteDC(hmemdc);
}
else
{
GlobalUnlock(hmem1);
GlobalFree(hmem1);
GlobalUnlock(hmem2);
GlobalFree(hmem2);
BitmapDataLoaded = FALSE;
}
}
}
return S_OK;
</PRE>
<PRE>}
</PRE>
<P>If the bitmap wasn't drawn because ReadyState is not READYSTATE_COMPLETE yet or
there was a problem with the bitmap, OnDraw() draws a solid background by using the
BackColor property, as shown in Listing 21.11. Add this code at the end of OnDraw(),
before the return statement. The SDK calls are very similar to the MFC calls used
in the MFC version of DieRoll--for example, ::OleTranslateColor() corresponds to
TranslateColor().</P>
<P>
<H4>Listing 21.11  CDieRoll::OnDraw()--Draw a Solid Background</H4>
<PRE> if (!drawn)
{
COLORREF back;
::OleTranslateColor(m_clrBackColor, NULL, &back);
HBRUSH backbrush = ::CreateSolidBrush(back);
::FillRect(di.hdcDraw, (RECT *)di.prcBounds, backbrush);
::DeleteObject(backbrush);
</PRE>
<PRE> }
</PRE>
<P>With the background drawn, as a bitmap image or a solid color, OnDraw() must now
tackle the foreground. Getting the foreground color is simple. Add these two lines
at the end of OnDraw() before the return statement:</P>
<P>
<PRE>COLORREF fore;
::OleTranslateColor(m_clrForeColor, NULL, &fore);
</PRE>
<P>The project should build successfully at this point if you want to be sure you've
entered all this code correctly.</P>
<P>If Dots is FALSE, the die should be drawn with a number on it. Add the code in
Listing 21.12 to OnDraw() before the return statement as usual. Again, the SDK functions
do the same job as the similarly named MFC functions used in the MFC version of DieRoll.</P>
<P>
<H4>Listing 21.12  CDieRoll::OnDraw()--Draw a Number</H4>
<PRE> if (!m_bDots)
{
_TCHAR val[20]; //character representation of the short value
_itot(m_sNumber, val, 10);
::SetTextColor(di.hdcDraw, fore);
::ExtTextOut(di.hdcDraw, 0, 0, ETO_OPAQUE,
(RECT *)di.prcBounds, val, _tcslen(val), NULL );
</PRE>
<PRE> }
</PRE>
<P>The code that draws dots is in Listing 21.13. Add it to OnDraw() before the return
statement to complete the function. This code is long but is explained in Chapter
17. As in the rest of OnDraw(), MFC function calls have been replaced with SDK calls.</P>
<P>
<H4>Listing 21.13  CDieRoll::OnDraw()--Draw Dots</H4>
<PRE> else
{
//dots are 4 units wide and high, one unit from the edge
int Xunit = width/16;
int Yunit = height/16;
int Xleft = width%16;
int Yleft = height%16;
// adjust top left by amount left over
int Top = di.prcBounds->top + Yleft/2;
int Left = di.prcBounds->left + Xleft/2;
HBRUSH forebrush;
forebrush = ::CreateSolidBrush(fore);
HBRUSH savebrush = (HBRUSH)::SelectObject(di.hdcDraw, forebrush);
switch(m_sNumber)
{
case 1:
::Ellipse(di.hdcDraw, Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //center
break;
case 2:
::Ellipse(di.hdcDraw, Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 3:
::Ellipse(di.hdcDraw, Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
::Ellipse(di.hdcDraw, Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //center
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 4:
::Ellipse(di.hdcDraw, Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //upper right
::Ellipse(di.hdcDraw, Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //lower left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 5:
::Ellipse(di.hdcDraw, Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //upper right
::Ellipse(di.hdcDraw, Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //center
::Ellipse(di.hdcDraw, Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //lower left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 6:
::Ellipse(di.hdcDraw, Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //upper right
::Ellipse(di.hdcDraw, Left+Xunit, Top+6*Yunit,
Left+5*Xunit, Top + 10*Yunit); //center left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+6*Yunit,
Left+15*Xunit, Top + 10*Yunit); //center right
::Ellipse(di.hdcDraw, Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //lower left
::Ellipse(di.hdcDraw, Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
}
::SelectObject(di.hdcDraw, savebrush);
::DeleteObject(forebrush);
</PRE>
<PRE> }
</PRE>
<P>Again, build the project to be sure you haven't missed anything. If you look in
your project folder now, you should see a file called DieRoll.htm (it doesn't show
up in FileView). This HTML is generated for you to test your control. Try loading
it into Internet Explorer now, and a die should display, as in Figure 21.11. It will
not have an image background and it will not roll when you click it.</P>
<P><A HREF="javascript:popUp('21uvc11.gif')"><B>FIG. 21.11</B></A><B> </B><I>Your
control can draw itself in a browser.</I></P>
<H2><I></I></H2>
<H2><A NAME="Heading15"></A>Persistence and a Property Page</H2>
<P>The properties have been added to the control and used in the drawing of the control.
Now all that remains is to make the properties persistent and to add a property page.</P>
<P>
<H3><A NAME="Heading16"></A>Adding a Property Page</H3>
<P>To add a property page to this control, follow these steps:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Choose Insert, New ATL Object from the menu bar to open the ATL Object
Wizard.
<P>
<DT></DT>
<DD><B>2. </B>Select Controls in the left pane and Property Page in the right pane;
then click Next.
<P>
<DT></DT>
<DD><B>3. </B>On the Names tab, enter <B>DieRollPPG</B> for the Short Name.
<P>
<DT></DT>
<DD><B>4. </B>Click the Strings tab (the settings on the Attributes tab will not
be changed). Enter <B>General</B> for the Title and <B>DieRoll Property Page</B>
for the Doc String. Blank out the Helpfile Name.
<P>
<DT></DT>
<DD><B>5. </B>Click OK to add the property page to the project.
<P>
</DL>
<P>Developer Studio will switch to ResourceView and open the dialog IDD_DIEROLLPPG.
Add a check box with the resource ID IDC_DOTS and the caption Display Dot Pattern
and an edit box with the resource ID <B>IDC_IMAGE</B> labelled Image URL, as shown
in Figure 21.12.</P>
<P>At the top of DieRollPPG.h, add this line:</P>
<P>
<PRE>#include "DieRollControl.h"
</PRE>
<P>You need to connect the controls on this property page to properties of the DieRoll
control. The first step is to add three lines to the message map in DieRollPPG.h
so that it resembles Listing 21.14.</P>
<P><A HREF="javascript:popUp('21uvc11a.gif')"><B>FIG. 21.12</B></A><B> </B><I>Add
two controls to the property page.</I></P>
<P><I></I>
<H4>Listing 21.14  DieRollPPG.h--Message Map</H4>
<PRE>BEGIN_MSG_MAP(CDieRollPPG)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_HANDLER(IDC_DOTS, BN_CLICKED, OnDotsChanged)
COMMAND_HANDLER(IDC_IMAGE, EN_CHANGE, OnImageChanged)
CHAIN_MSG_MAP(IPropertyPageImpl<CDieRollPPG>)
</PRE>
<PRE>END_MSG_MAP()
</PRE>
<P>These new lines ensure that OnInitDialog() will be called when the dialog box
is initialized and that OnDotsChanged() or OnImageChanged() will be called whenever
Dots or Image are changed (the other properties don't have put methods and so can't
be changed).</P>
<P>Add the code in Listing 21.15 to the header file to declare and implement OnInitDialog().
Put it after the constructor, so it will be public as well.</P>
<P>
<H4>Listing 21.15  DieRollPPG.h--CDieRollPPG::OnInitDialog()</H4>
<PRE> LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL & bHandled)
{
USES_CONVERSION;
CComQIPtr<IDieRoll, &IID_IDieRoll> pDieRoll(m_ppUnk[0]);
BOOL dots;
pDieRoll->get_Dots(&dots);
::SendDlgItemMessage(m_hWnd, IDC_DOTS, BM_SETCHECK, dots, 0L);
BSTR image;
pDieRoll->get_Image(&image);
LPTSTR image_URL = W2T(image);
SetDlgItemText(IDC_IMAGE, image_URL);
return TRUE;
</PRE>
<PRE> }
</PRE>
<P>This code begins by declaring a pointer to an IDieRoll interface using the CComQIPtr
template class and initializing it to the first element of the m_ppUnk array in this
class, CDieRollPPG. (A property page can be associated with multiple controls.) The
constructor for the CComQIPtr template class uses the QueryInterface() method of
the IUnknown pointer that was passed in to the constructor to find a pointer to an
IDieRoll interface. Now you can call member functions of this interface to access
the properties of the DieRoll control.</P>
<P>Finding the value of the Dots property of the CDieRoll object is simple enough:
Call get_Dots(). To use that value to initialize the check box on the property page,
send a message to the control using the SDK function ::SendDlgItemMessage(). The
BM_SETCHECK parameter indicates that you are setting whether the box is checked (selected).
Passing dots as the fourth parameter ensures that IDC_DOTS will be selected if dots
is TRUE and deselected if dots is FALSE. Similarly, obtain the URL for the image
with get_Image(), convert it from wide characters, and then use SetDlgItemText()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -