?? ch20.htm
字號:
<P><A HREF="javascript:popUp('20uvc05.gif')"><B>FIG. 20.5</B></A><B> </B><I>Create
a new class to handle asynchronous properties.</I></P>
<P>The reason that the new class should inherit from CCachedDataProperty is that
it will load the property information into a file, which is an easier way to handle
the bitmap. If the control has a property that was downloaded because it changed
often (for example, current weather), CDataPathProperty would be a better choice.</P>
<P><B>Add the <I>Image</I> Property to <I>CDierollCtrl</I></B><I>  </I>With
the new CDierollDataPathProperty class added to the Dieroll control, add the property
to the original CDierollCtrl class that you copied: In ClassWizard, on the Automation
tab, make sure that CDierollCtrl is selected in the far right drop-down box. Click
Add Property and fill out the dialog as shown in Figure 20.6. The external name you
choose is the one that will appear in the HTML: Image is simple and doesn't require
a lot of typing. The type should be BSTR--that choice won't be in the drop-down box
for type until you change the Implementation to Get/Set Methods.</P>
<P><A HREF="javascript:popUp('20uvc06.gif')"><B>FIG. 20.6</B></A><B> </B><I>The image
file is added as a BSTR property.</I></P>
<P>ClassWizard adds the Get and Set functions to your control class, but the TODO
comments (see Listing 20.6) are cryptic.</P>
<P>
<H4>Listing 20.6  DierollCtl.cpp--Get and Set Functions</H4>
<PRE>BSTR CDierollCtrl::GetImage()
{
CString strResult;
// TODO: Add your property handler here
return strResult.AllocSysString();
}
void CDierollCtrl::SetImage(LPCTSTR lpszNewValue)
{
// TODO: Add your property handler here
SetModifiedFlag();
</PRE>
<PRE>}
</PRE>
<P>As with other Get and Set properties, you'll have to add a member variable to
the control class and add code to these functions to get or set its value. It is
an instance of the new CDierollDataPathProperty class. Right-click CDierollCtrl in
ClassView and choose Add Member Variable. Figure 20.7 shows how to fill in the dialog
box to declare the member variable mdpp_image. (The dpp in the name is to remind
you that this is a data path property.)</P>
<P><A HREF="javascript:popUp('20uvc07.gif')"><B>FIG. 20.7</B></A><B> </B><I>The image
file member variable is an instance of the new class.</I></P>
<P>Now you can finish the Get and Set functions, as shown in Listing 20.7.</P>
<P>
<H4>Listing 20.7  DierollCtl.cpp--Completed Get and Set Functions</H4>
<PRE>BSTR CDierollCtrl::GetImage()
{
CString strResult;
strResult = mdpp_image.GetPath();
return strResult.AllocSysString();
}
void CDierollCtrl::SetImage(LPCTSTR lpszNewValue)
{
Load(lpszNewValue, mdpp_image);
SetModifiedFlag();
</PRE>
<PRE>}
</PRE>
<P>At the top of the header file for CDierollCtrl, add this include statement:</P>
<P>
<PRE>#include "DierollDataPathProperty.h"
</PRE>
<P>Now there are some bits and pieces to deal with because you are changing an existing
control rather than turning on asynchronous properties when you first built Dieroll.
First, in CDierollCtrl::DoPropExchange(), arrange persistence and initialization
for mdpp_image by adding this line:</P>
<P>
<PRE>PX_DataPath( pPX, _T("Image"), mdpp_image);
</PRE>
<P>Second, add a line to the stub of CDierollCtrl::OnResetState() that ClassWizard
provided, to reset the data path property when the control is reset. Listing 20.8
shows the function.</P>
<P>
<H4>Listing 20.8  DierollCtl.cpp--CDierollCtrl::OnResetState()</H4>
<PRE>/////////////////////////////////////////////////////////////////////////////
// CDierollCtrl::OnResetState - Reset control to default state
void CDierollCtrl::OnResetState()
{
COleControl::OnResetState(); // Resets defaults found in DoPropExchange
mdpp_image.ResetData();
</PRE>
<PRE>}
</PRE>
<P><B>Add the <I>ReadyStateChange</I> Event and the <I>ReadyState</I> Property  </B>Use
ClassWizard to add the stock event ReadyStateChange. In ClassWizard, click the ActiveX
Events tab, then the Add Event button. Choose ReadyStateChange from the drop-down
box and click OK. Figure 20.8 shows the Add Event dialog box for this event. Events,
as discussed in Chapter 17, notify the control's container that something has happened
within the control. In this case, what has happened is that the rest of the control's
data has arrived and the control's state of readiness has changed.</P>
<P><A HREF="javascript:popUp('20uvc08.gif')"><B>FIG. 20.8</B></A><B> </B><I>Add a
stock event to notify the container of a change in the control's readiness.</I></P>
<P>Use ClassWizard to add a property to CDierollCtrl for the ready state. In ClassWizard,
click the Automation tab, then the Add Property button. Choose ReadyState from the
drop-down box, and because this is a stock property, the rest of the dialog box is
filled in for you, as shown in Figure 20.9. Click OK to finish adding the property
and then close ClassWizard. ClassWizard doesn't add a stub function for GetReadyState()
because CDierollCtrl will inherit this from COleControl.</P>
<P><A HREF="javascript:popUp('20uvc09.gif')"><B>FIG. 20.9</B></A><B> </B><I>Add a
stock property to track the control's readiness.</I></P>
<P>Add code to the constructor to connect the cached property to this control and
to initialize the member variable in COleControl that is used in COleControl::GetReadyState()
and set by COleControl::InternalSetReadyState(). Because the control can be used
right away, the readiness state should start at READYSTATE_INTERACTIVE. Listing 20.9
shows the new constructor.</P>
<P>
<H4>Listing 20.9  DierollCtl.cpp--CDierollCtrl::CDierollCtrl()</H4>
<PRE>CDierollCtrl::CDierollCtrl()
{
InitializeIIDs(&IID_DDieroll, &IID_DDierollEvents);
mdpp_image.SetControl(this);
m_lReadyState = READYSTATE_INTERACTIVE;
</PRE>
<PRE>}
</PRE>
<P><B>Implement <I>CDierollDataPathProperty  </I></B>There is some work
to do in<B> </B>CDierollDataPathProperty before changing CDierollCtrl::OnDraw().
This class loads a bitmap, and this chapter isn't going to explain most of what's
involved in reading a BMP file into a CBitmap object. The most important function
is OnDataAvailable(), which is in Listing 20.10. Add this function to the class by
right-clicking CDierollCtrl in ClassView and choosing Add Virtual Function. Select
OnDataAvailable from the list on the left, and click Add and Edit; then type this
code.</P>
<P>
<H4>Listing 20.10  DierollDataPathProperty.cpp--OnDataAvailable()</H4>
<PRE>void CDierollDataPathProperty::OnDataAvailable(DWORD dwSize, DWORD grfBSCF)
{
CCachedDataPathProperty::OnDataAvailable(dwSize, grfBSCF);
if(grfBSCF & BSCF_LASTDATANOTIFICATION)
{
m_Cache.SeekToBegin();
if (ReadBitmap(m_Cache))
{
BitmapDataLoaded = TRUE;
// safe because this control has only one property:
GetControl()->InternalSetReadyState(READYSTATE_COMPLETE);
GetControl()->InvalidateControl();
}
}
</PRE>
<PRE>}
</PRE>
<P>Every time a block of data is received from the remote site, this function is
called. The first line of code uses the base class version of the function to deal
with that block and set the flag called grfBSCF. If, after dealing with the latest
block, the download is complete, the ReadBitmap() function is called to read the
cached data into a bitmap object that can be displayed as the control background.
(The code for ReadBitmap() isn't presented or discussed here, though it is on the
Web site for you to copy into your application.) After the bitmap has been read,
the control's ready state is complete and the call to InvalidateControl() arranges
for a redraw.</P>
<P><B>Revise <I>CDierollCtrl::OnDraw()  </I></B>The structure of CDierollCtrl::OnDraw()<B>
</B>was laid out long ago. In this block of code, the background is filled in before
the code that checks whether to draw dots or a number:</P>
<P>
<PRE> COLORREF back = TranslateColor(GetBackColor());
CBrush backbrush;
backbrush.CreateSolidBrush(back);
pdc->FillRect(rcBounds, &backbrush);
</PRE>
<P>Replace that block with the one in Listing 20.11.</P>
<P>
<H4>Listing 20.11  DierollDataPathProperty.cpp--New Code for OnDraw()</H4>
<PRE> CBrush backbrush;
BOOL drawn = FALSE;
if (GetReadyState() == READYSTATE_COMPLETE)
{
CBitmap* image = mdpp_image.GetBitmap(*pdc);
if (image)
{
CDC memdc;
memdc.CreateCompatibleDC(pdc);
memdc.SelectObject(image);
BITMAP bmp; // just for height and width
image->GetBitmap(&bmp);
pdc->StretchBlt(0, // upper left
0, // upper right
rcBounds.Width(), // target width
rcBounds.Height(), // target height
&memdc, // the image
0, // offset into image -x
0, // offset into image -y
bmp.bmWidth, // width
bmp.bmHeight, // height
SRCCOPY); // copy it over
drawn = TRUE;
}
}
if (!drawn)
{
COLORREF back = TranslateColor(GetBackColor());
backbrush.CreateSolidBrush(back);
pdc->FillRect(rcBounds, &backbrush);
</PRE>
<PRE> }
</PRE>
<P>The BOOL variable drawn ensures that if the control is complete, but something
goes wrong with the attempt to use the bitmap, the control will be drawn the old
way. If the control is complete, the image is loaded into a CBitmap* and then drawn
into the device context. Bitmaps can only be selected into a memory device context
and then copied over to an ordinary device context. Using StretchBlt() will stretch
the bitmap during the copy, though a sensible Web page designer will have specified
a bitmap that matches the HEIGHT and WIDTH attributes of the OBJECT tag. The old
drawing code is still here, used if drawn remains FALSE.</P>
<P>
<H3><A NAME="Heading10"></A>Testing and Debugging Dieroll</H3>
<P>Having made all those changes, build the control, which will register it. One
way to test it would be to bring up that HTML page in Explorer again, but you might
prefer to debug the control. It is possible to debug a control even though you can't
run it standalone. Normally, a developer would arrange to debug the control in the
test container, but you can use any application that can contain the control.</P>
<P>In Developer Studio, choose Project Settings. Click the Debug tab and make sure
that all the lines in the far left list box are selected. Select General in the top
drop-down box, and in the edit box labeled Executable for Debug Session, enter the
full path to Microsoft Internet Explorer on your computer. (If there's a shorcut
to Microsoft Internet Explorer on your desktop, right-click it and choose Properties
to get the path to the executable. Otherwise, use the Find utility on the Start menu
to find iexplore.exe. Figure 20.10 shows an example.) Now when you choose Build,
Start Debug, Go or click the Go toolbar button, Explorer will launch. Open a page
of HTML that loads the control, and the control will run in the debugger. You can
set breakpoints, step through code, and examine variables, just as with any other
application.</P>
<P><A HREF="javascript:popUp('20uvc10.gif')"><B>FIG. 20.10</B></A><B> </B><I>Arrange
to run Explorer when you debug the control.</I></P>
<P>Here's the syntax for an OBJECT tag that sets the Image property:</P>
<P>
<PRE><OBJECT
CLASSID="clsid:46646B43-EA16-11CF-870C-00201801DDD6"
CODEBASE="http://www.gregcons.com/test/dieroll.ocx"
ID=die1
WIDTH=200
HEIGHT=200
ALIGN=center
HSPACE=0
VSPACE=0
>
<PARAM NAME="Dots" VALUE="1">
<PARAM NAME="Image" VALUE="http://www.gregcons.com/test/beans.bmp">
If you see this text, your browser does not support the OBJECT tag. </BR>
</OBJECT>
</PRE>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>TIPP:</STRONG> Remember, don't just copy these HTML samples to your own machine
if you are building Dieroll yourself. You need to use your own CLSID, an URL to the
location of your copy of the OCX, and the image file you are using.
<HR>
</BLOCKQUOTE>
<P>Figure 20.11 shows the control with a background image of jelly beans. It takes
30-60 seconds to load this 40KB image through the Web, and while it is loading, the
control is perfectly usable as a plain die with no background image. That's the whole
point of asynchronous properties, and that's what all the effort of the previous
sections achieves.</P>
<P><A HREF="javascript:popUp('20uvc11.gif')"><B>FIG. 20.11</B></A><B> </B><I>Now
the die displays on a field of jelly beans or on any other image you choose.</I></P>
<H1><I></I></H1>
<CENTER>
<P>
<HR>
<A HREF="../ch19/ch19.htm"><IMG SRC="../button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="../ch21/ch21.htm"><IMG
SRC="../button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR>
</P>
<P>© <A HREF="../copy.htm">Copyright</A>, Macmillan Computer Publishing. All
rights reserved.
</CENTER>
</BODY>
</HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -