📄 ch09.htm
字号:
<P>Add the initialization of the <TT>m_lReadyState</TT> member variable to the constructor
of the <TT>CATLControlWin</TT> class (see Listing 9.3).
<H3><A NAME="Heading6"></A>Listing 9.3 <SPACER TYPE="HORIZONTAL" SIZE="10">ATLCONTROLWIN.H--Initialize
the m_lReadyState Member Variable in the Class Constructor</H3>
<P><FONT COLOR="#0066FF"><TT>CATLControlWin() <BR>
{ <BR>
. . . <BR>
// set the initial state of the ReadyState property <BR>
m_lReadyState = READYSTATE_LOADING; <BR>
} </TT></FONT></P>
<P>The last step to implement is the <TT>get_ReadyState</TT> function, which simply
returns the current value of the <TT>m_lReadyState</TT> member variable (see Listing
9.4).
<H3><A NAME="Heading7"></A>Listing 9.4 <SPACER TYPE="HORIZONTAL" SIZE="10">ATLCONTROLWIN.CPP--Implement
the get_ReadyState Function to Return the Current ReadyState Value</H3>
<P><FONT COLOR="#0066FF"><TT>STDMETHODIMP CATLControlWin::get_ReadyState(long * pVal)
<BR>
{ <BR>
// set the return value to the value of the member variable <BR>
*pVal = m_lReadyState; <BR>
return S_OK; <BR>
} </TT></FONT></P>
<P>The next step is to add support for the <TT>ReadyStateChange</TT> event. Open
the ATLControl.idl file, and add the <TT>ReadyStateChange</TT> function to the event
interface that is added in <A HREF="ch08.htm">Chapter 8</A> (see Listing 9.5).
<H3><A NAME="Heading8"></A>Listing 9.5<SPACER TYPE="HORIZONTAL" SIZE="10"> ATLCONTROL.IDL--Add
the ReadyStateChange Event to the IDL File</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
[ <BR>
uuid(C31D4C71-7AD7-11d0-BEF6-00400538977D), <BR>
helpstring("ATLControlWin Event Interface") <BR>
] <BR>
dispinterface _DATLControlWin <BR>
{ <BR>
properties: <BR>
methods: <BR>
[id(1)] void Change([in, out]BSTR * bstrCaption, <BR>
[in, out] long * lAlignment); <BR>
[id(DISPID_READYSTATECHANGE)] void ReadyStateChange(); <BR>
}; <BR>
<BR>
. . . </TT></FONT></P>
<P>Remember that support for events is not automatic in ATL, so you must manually
rebuild the CPATLControl.h file that was created in <A HREF="ch08.htm">Chapter 8</A>
for your connection point support by using the ATL Proxy Generator. To update the
file follow these steps:
<OL>
<LI>Compile the IDL file since the event interface header file is built from the
type library.
<P>
<LI>From the Project menu, select the Add to Project menu item, and then select the
Components and Controls submenu item.
<P>
<LI>In the Components and Controls Gallery dialog, double-click the Developer Studio
Components folder.
<P>
<LI>After the Components and Controls Gallery dialog is refreshed with data, double-click
the ATL Proxy Generator icon.
<P>
<LI>Click OK to close the Insert the ProxyGen Component dialog.
<P>
<LI>Click the ... button to display the Open dialog. Select the ATLControl.tlb file,
and click Open.
<P>
<LI>Select the <TT>_DATLControlWin</TT> entry in the <U>N</U>ot Selected list box,
and click the > button to move the entry to the <U>S</U>elected list box. Ensure
that the Proxy <U>T</U>ype is set to Connection Point and click <U>I</U>nsert.
<P>
<LI>A Save dialog appears with the file CPATLControl.h in the File <U>n</U>ame edit
box. Click <U>S</U>ave to continue. Click Yes to replace the existing CPATLControl.h
file.
<P>
<LI>Click OK in the confirmation dialog that indicates the operation was successful.
<P>
<LI>Click Close in the ATL Proxy Generator and Components and Controls Gallery dialogs.
</OL>
<P>The <TT>Fire_ReadyStateChange</TT> method is now added to the <TT>CProxy_DATLControlWin</TT>
class.</P>
<P>Asynchronous properties are based on URLs and not on the data type of the data
to be downloaded, for example, a bitmap or text file. The URL is stored in a string
property of the control. For the sample implementation, you add the property called
<TT>TextDataPath</TT> to the control. From the ClassView tab in the Project Workspace
window, select the <TT>IATLControlWin</TT> interface, click the right mouse button,
and select the Add <U>P</U>roperty... menu item.</P>
<P>In the Add Property to Interface dialog, set the Property <U>T</U>ype to BSTR,
the Property <U>N</U>ame to <TT>TextDataPath</TT>, and leave the remainder of the
settings at their default values (see fig. 9.2). Click OK to confirm the entry and
close the dialog. <B><BR>
<BR>
</B><A HREF="Art/09/xfigs02.jpg"><B>FIG. 9.2</B></A> <I><BR>
Add the <TT>TextDataPath </TT>property to the control using the ATL ClassWizard.</I></P>
<P>Add the <TT>dispidTextDataPath</TT> constant to the <TT>PROPDISPIDS</TT> enumeration
in the ATLControl.idl file, and update the <TT>TextDataPath</TT> function to use
the constant value (see Listing 9.6).
<H3><A NAME="Heading9"></A>Listing 9.6 <SPACER TYPE="HORIZONTAL" SIZE="10">ATLCONTROL.IDL--Add
the dispidTextDataPath Enumeration to the IDL File</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
typedef enum propdispids <BR>
{ <BR>
dispidAlignment = 2, <BR>
dispidCaptionProp = 3, <BR>
dispidTextDataPath = 4, <BR>
}PROPDISPIDS; <BR>
[ <BR>
object, <BR>
uuid(A19F6963-7884-11D0-BEF3-00400538977D), <BR>
dual, <BR>
helpstring("IATLControlWin Interface"), <BR>
pointer_default(unique) <BR>
] <BR>
interface IATLControlWin : IDispatch <BR>
{ <BR>
[id(1), helpstring("method CaptionMethod")] HRESULT CaptionMethod( <BR>
[in] BSTR bstrCaption, [in, optional] VARIANT varAlignment, <BR>
[out, retval] long * lRetVal); <BR>
[propget, id(dispidTextDataPath), helpstring("property TextDataPath")]
<BR>
HRESULT TextDataPath([out, retval] BSTR *pVal); <BR>
[propput, id(dispidTextDataPath), helpstring("property TextDataPath")]
<BR>
HRESULT TextDataPath([in] BSTR newVal); <BR>
. . . </TT></FONT></P>
<P>The TextDataPath property is used to store the URL of the data that the property
represents. To complete your implementation of the property, add the member variable,
<TT>m_bstrTextDataPath</TT> (see Listing 9.7). The data for the member is declared
as the type <TT>CComBSTR</TT>, which is a <TT>BSTR</TT> wrapper class provided with
ATL. See the ATL documentation for more information. The use of the <TT>CComBSTR</TT>
data type versus a standard <TT>BSTR</TT> or <TT>LPTSTR</TT> is purely an arbitrary
decision on your part and is based on your implementation requirements. We used <TT>CComBSTR</TT>
to demonstrate the different implementation styles available to you with ATL. You
also add the member variable <TT>m_bstrText</TT>, also of the type <TT>CComBSTR</TT>,
to store the data as it is supplied to the control and the member function OnData,
which will be the callback function that receives the data as it is downloaded. We
will discuss these two members a little later in this chapter.
<H3><A NAME="Heading10"></A>Listing 9.7<SPACER TYPE="HORIZONTAL" SIZE="10"> ATLCONTROLWIN.H--The
m_bstrTextDataPath Member Variable Is Added to the Class Declaration to Store the
TextDataPath Property</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
//OnData will be used as a callback functin by the CBindStatusCallback object. <BR>
//OnData will be called periodically with data from the asynchronous transfer <BR>
void OnData(CBindStatusCallback<CATLControlWin>* pbsc, BYTE* pBytes, DWORD
dwSize); <BR>
protected: <BR>
. . . <BR>
// for the ReadyState property <BR>
long m_lReadyState; <BR>
// for the TextDataPath property <BR>
CComBSTR m_bstrTextDataPath; <BR>
// to hold the data as it is passed in <BR>
CComBSTR m_bstrText; <BR>
}; <BR>
#endif //__ATLCONTROLWIN_H_ </TT></FONT></P>
<P>The implementation of the <TT>get_TextDataPath/put_TextDataPath</TT> function
is where the asynchronous data transfer of the property takes place (see Listing
9.8). The <TT>get_TextDataPath</TT> function returns the current value stored in
the <TT>m_bstrTextDataPath</TT> member variable. The <TT>put_TextDataPath</TT> function
stores the new location of the data and then initiates a transfer of the data to
the control with a call to <TT>CBindStatusCallback<CATLControlWin>::Download
(. . .)</TT>. <TT>CBindStatusCallback</TT> is an ATL wrapper class that wraps the
<TT>IBindStatusCallback</TT> interface. <TT>CBindStatusCallback</TT> handles all
of the details of the data transfer and only requires that you implement a function,
in this case <TT>OnData</TT>, to receive the data as it is downloaded. The <TT>OnData</TT>
function is supplied as the second parameter to the <TT>Download</TT> function and
must conform to the prototype defined by ATL. See the ATL documentation on the <TT>Download</TT>
function for more information.
<H3><A NAME="Heading11"></A>Listing 9.8<SPACER TYPE="HORIZONTAL" SIZE="10"> ATLCONTROLWIN.CPP--Implementation
of the get_TextDataPath /put_TextDataPath Functions</H3>
<P><FONT COLOR="#0066FF"><TT>STDMETHODIMP CATLControlWin::get_TextDataPath(BSTR *
pVal) <BR>
{ <BR>
// return a copy of the member variable <BR>
*pVal = m_bstrTextDataPath.Copy(); <BR>
return S_OK; <BR>
} <BR>
STDMETHODIMP CATLControlWin::put_TextDataPath(BSTR newVal) <BR>
{ <BR>
HRESULT hResult = S_OK; <BR>
// copy the new string to the member variable <BR>
m_bstrTextDataPath = newVal; <BR>
// clear the data buffer <BR>
m_bstrText = _T(""); <BR>
// start the asynchronous download of the data <BR>
CBindStatusCallback<CATLControlWin>::Download(this, OnData, m_bstrTextDataPath,
<BR>
m_spClientSite, FALSE); <BR>
// let the container know that the property has changed <BR>
this->SetDirty(TRUE); <BR>
// this->SetModifiedFlag(); <== MFC version <BR>
return hResult; <BR>
} </TT></FONT></P>
<P><TT>OnData</TT> is a very basic implementation of the asynchronous data transfer
mechanism provided by the <TT>IBindStatusCallback</TT> interface and the <TT>CBindStatusCallback</TT>
class (see Listing 9.9). If the <TT>OnUData</TT> function is called, the new data
is appended to the <TT>m_bstrText</TT> member variable, and the <TT>CaptionMethod</TT>
is called. Throughout the <TT>OnData</TT> implementation, note the use of the <TT>CBindStatusCallback</TT>
members to determine the status of the current call to <TT>OnData</TT>. Also note
the use of the <TT>ReadyState</TT> property to indicate to the container application
that an asynchronous download is taking place and when it has finished. The BaseCtl
sample in <A HREF="ch10.htm">Chapters 10</A> and <A HREF="ch11.htm">11</A> demonstrates
how to implement asynchronous properties using the <TT>OnDataAvailable</TT> function.
<TT>OnDataAvailable</TT> gives you more information about the download of the data
and how it is taking place.
<H3><A NAME="Heading12"></A>Listing 9.9<SPACER TYPE="HORIZONTAL" SIZE="10"> ATLCONTROLWIN.CPP--OnData
Function Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>//OnData will be used as a callback functin by the CBindStatusCallback
object. <BR>
//OnData will be called periodically with data from the asynchronous transfer <BR>
void CATLControlWin::OnData(CBindStatusCallback<CATLControlWin>* pbsc, BYTE*
pBytes, <BR>
DWORD dwSize) <BR>
{ <BR>
// if we have not read any data yet <BR>
if(pbsc->m_dwTotalRead == 0) <BR>
{ <BR>
// clear the buffer <BR>
m_bstrText = _T(""); <BR>
// set the ready state of the control <BR>
m_lReadyState = READYSTATE_LOADING; <BR>
// let the container know that the property has changed <BR>
this->Fire_ReadyStateChange(); <BR>
} <BR>
// add the data to our buffer <BR>
m_bstrText.Append((LPCSTR) pBytes); <BR>
long lRetVal; <BR>
VARIANT varAlignment; <BR>
// initialize the variant <BR>
::VariantInit(&varAlignment); <BR>
<BR>
// defer to the CaptionMethod implementation <BR>
this->CaptionMethod(m_bstrText, varAlignment, &lRetVal); <BR>
// if the function returned success <BR>
if(TRUE == lRetVal) <BR>
// let the control know that the property has changed <BR>
this->SetDirty(TRUE); <BR>
// this->SetModifiedFlag(); <== MFC version <BR>
// if there is nothing left <BR>
if(pbsc->m_dwAvailableToRead == 0) <BR>
{ <BR>
// set the ready state of the control <BR>
m_lReadyState = READYSTATE_COMPLETE; <BR>
// let the container know that the property has changed <BR>
this->Fire_ReadyStateChange(); <BR>
} <BR>
} </TT></FONT></P>
<P>The final touch of your asynchronous property implementation is to add the property
to the persistence macro in your class declaration (see Listing 9.10). Do not add
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -