📄 ch11.htm
字号:
<BR>
STDMETHOD(get_Alignment)(THIS_ long FAR* Value);<BR>
<BR>
STDMETHOD(put_Alignment)(THIS_ long Value);<BR>
<BR>
STDMETHOD(get_BackColor)(THIS_ OLE_COLOR FAR* Value);<BR>
<BR>
STDMETHOD(put_BackColor)(THIS_ OLE_COLOR Value);<BR>
<BR>
STDMETHOD(get_ReadyState)(THIS_ long FAR* Value);<BR>
<BR>
STDMETHOD(get_TextDataPath)(THIS_ BSTR FAR* bstrRetVal);<BR>
<BR>
STDMETHOD(put_TextDataPath)(THIS_ BSTR Value);<BR>
<BR>
STDMETHOD(CaptionMethod)(THIS_ BSTR bstrCaption, VARIANT varAlignment, <BR>
<BR>
long FAR* lRetVal);<BR>
<BR>
STDMETHOD(get_CaptionProp)(THIS_ VARIANT varAlignment, BSTR FAR* bstrRetVal);<BR>
<BR>
STDMETHOD(put_CaptionProp)(THIS_ VARIANT varAlignment, BSTR lpszNewValue);<BR>
<BR>
STDMETHOD_(void, AboutBox)(THIS); <BR>
. . . <BR>
// private state information.<BR>
<BR>
//<BR>
<BR>
BCFCONTROLCTLSTATE m_state; <BR>
protected:<BR>
<BR>
LPTSTR m_lptstrCaption;<BR>
<BR>
LPTSTR m_lptstrTextDataPath; <BR>
};<BR>
</TT></FONT></P>
<P>Listing 11.7 shows your member variable initialization in your constructor. You
don't need to set any default values since the <TT>ReadyState</TT> will not be persisted
across execution lifetimes.
<H3><A NAME="Heading10"></A>Listing 11.7<SPACER TYPE="HORIZONTAL" SIZE="10"> BCFCONTROLCTL.CPP--Member
Initialization</H3>
<P><FONT COLOR="#0066FF"><TT>#pragma warning(disable:4355) // using `this' in constructor<BR>
<BR>
CBCFControlControl::CBCFControlControl<BR>
<BR>
(<BR>
<BR>
IUnknown *pUnkOuter<BR>
<BR>
)<BR>
<BR>
: CInternetControl(pUnkOuter, OBJECT_TYPE_CTLBCFCONTROL, (IDispatch *)this)<BR>
<BR>
{<BR>
<BR>
// initialize anything here ...<BR>
<BR>
// <BR>
... <BR>
// set the ready state of the control<BR>
<BR>
m_state.lReadyState = READYSTATE_LOADING; <BR>
// NULL terminate the string reference<BR>
<BR>
m_lptstrTextDataPath = new TCHAR[1];<BR>
<BR>
m_lptstrTextDataPath[0] = `\0'; <BR>
} <BR>
#pragma warning(default:4355) // using `this' in constructor<BR>
</TT></FONT></P>
<P>Last you add your implementation of the <TT>ReadyState</TT> and <TT>TextDataPath</TT>
methods to your class source file (see Listing 11.8). You initiate the asynchronous
download of your data within your <TT>put_TextDataPath</TT> method. You do this through
a call to <TT>SetupDownload</TT>, where you pass in the path of the data to be downloaded
and the dispid of the property that the data is bound to. As data becomes available,
your <TT>OnData</TT> method is called.
<H3><A NAME="Heading11"></A>Listing 11.8 <SPACER TYPE="HORIZONTAL" SIZE="10">BCFCONTROLCTL.CPP--Property
Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>STDMETHODIMP CBCFControlControl::get_ReadyState(long
* Value)<BR>
<BR>
{<BR>
<BR>
// make sure that we have a good pointer<BR>
<BR>
CHECK_POINTER(Value); <BR>
// set the return value<BR>
<BR>
*Value = m_state.lReadyState; <BR>
// return the result<BR>
<BR>
return S_OK;<BR>
<BR>
} <BR>
STDMETHODIMP CBCFControlControl::get_TextDataPath(BSTR FAR * bstrRetVal)<BR>
<BR>
{<BR>
<BR>
// if there is a string<BR>
<BR>
if(*bstrRetVal);<BR>
<BR>
{<BR>
<BR>
// free the string because we are going to replace it<BR>
<BR>
::SysFreeString(*bstrRetVal); <BR>
// clear the reference just to be safe<BR>
<BR>
*bstrRetVal = NULL;<BR>
<BR>
} <BR>
// return the caption as a BSTR<BR>
<BR>
*bstrRetVal = ::SysAllocString(OLESTRFROMANSI(m_lptstrTextDataPath)); <BR>
return S_OK;<BR>
<BR>
} <BR>
STDMETHODIMP CBCFControlControl::put_TextDataPath(BSTR bstrNewValue)<BR>
<BR>
{<BR>
<BR>
HRESULT hResult = S_OK; <BR>
// get a ANSI string from the BSTR<BR>
<BR>
MAKE_ANSIPTR_FROMWIDE(lpctstrTextDataPath, bstrNewValue); <BR>
// if we have a string<BR>
<BR>
if(lpctstrTextDataPath != NULL)<BR>
<BR>
{<BR>
<BR>
// if we have a string<BR>
<BR>
if(m_lptstrTextDataPath)<BR>
<BR>
{<BR>
<BR>
// delete the existing string<BR>
<BR>
delete [] m_lptstrTextDataPath; <BR>
// clear the reference just to be safe<BR>
<BR>
m_lptstrTextDataPath = NULL;<BR>
<BR>
} <BR>
// allocate a new string<BR>
<BR>
m_lptstrTextDataPath = new TCHAR[lstrlen(lpctstrTextDataPath) + 1]; <BR>
// assign the string to our member variable<BR>
<BR>
lstrcpy(m_lptstrTextDataPath, lpctstrTextDataPath);<BR>
<BR>
} <BR>
// start the asynchronous download of the data<BR>
<BR>
this->SetupDownload(OLESTRFROMANSI(m_lptstrTextDataPath), dispidTextDataPath);
<BR>
// let the container know that the property has changed<BR>
<BR>
m_fDirty = TRUE;<BR>
<BR>
// this->SetModifiedFlag(); <== MFC version <BR>
return hResult; <BR>
}<BR>
</TT></FONT></P>
<P>Listing 11.9 shows your implementation of your <TT>OnData</TT> method, which will
progressively render the caption of your control from the <TT>IStream</TT> supplied.
It is possible to receive the <I>first </I>and<I> last</I> notification messages
within a single call to <TT>OnData</TT>, which is the reason for the separation between
the <TT>BSCF_FIRSTDATANOTIFICATION</TT> and <TT>BSCF_LASTDATANOTIFICATION</TT> messages.
Another flag that can be passed to the function is <TT>BSCF_INTERMEDIATEDATANTIFICATION</TT>,
which indicates that additional data is to be passed and that you have not received
it all. Your <TT>OnData</TT> function assumes that multiple calls to load data will
be made and checks for only the first and last notification messages.
<H3><A NAME="Heading12"></A>Listing 11.9<SPACER TYPE="HORIZONTAL" SIZE="10"> BCFCONTROLCTL.CPP--OnData
Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>HRESULT CBCFControlControl::OnData(DISPID propId, DWORD
bscfFlag, IStream * strm, DWORD dwSize)<BR>
<BR>
{<BR>
<BR>
HRESULT hr = NOERROR; <BR>
// if this is the first notification<BR>
<BR>
if(bscfFlag & BSCF_FIRSTDATANOTIFICATION)<BR>
<BR>
{<BR>
<BR>
// if we have a reference<BR>
<BR>
if(m_lptstrCaption)<BR>
<BR>
{<BR>
<BR>
// delete the string buffer<BR>
<BR>
delete [] m_lptstrCaption; <BR>
// clear the reference just to be safe<BR>
<BR>
m_lptstrCaption = NULL;<BR>
<BR>
}<BR>
<BR>
} <BR>
// alloc a temp buffer<BR>
<BR>
LPTSTR lptstrTempBuffer = new TCHAR[dwSize + 1];<BR>
<BR>
<BR>
<BR>
ULONG ulBytesRead;<BR>
<BR>
// read the data to a temp buffer<BR>
<BR>
hr = strm->Read(lptstrTempBuffer, dwSize, &ulBytesRead); <BR>
// if we read in any data<BR>
<BR>
if(hr == S_OK && ulBytesRead)<BR>
<BR>
{<BR>
<BR>
// null terminate the amount of data the was actually read in<BR>
<BR>
lptstrTempBuffer[ulBytesRead] = `\0'; <BR>
// get a new buffer with enough space to hold all of the data<BR>
<BR>
LPTSTR lptstrNewBuffer = new TCHAR[lstrlen(m_lptstrCaption) + ulBytesRead + 1]; <BR>
// copy the existing data to the new buffer<BR>
<BR>
lstrcpy(lptstrNewBuffer, m_lptstrCaption); <BR>
// add the new data to the buffer<BR>
<BR>
lstrcat(lptstrNewBuffer, lptstrTempBuffer); <BR>
// remove the existing string<BR>
<BR>
delete [] m_lptstrCaption; <BR>
// copy the data to the buffer<BR>
<BR>
m_lptstrCaption = lptstrNewBuffer; <BR>
// set the dirty flag<BR>
<BR>
m_fDirty = TRUE; <BR>
// redraw the control<BR>
<BR>
this->InvalidateControl(NULL);<BR>
<BR>
} <BR>
// if this is our last notification<BR>
<BR>
if(bscfFlag & BSCF_LASTDATANOTIFICATION)<BR>
<BR>
{<BR>
<BR>
// set the ready state of the control<BR>
<BR>
m_state.lReadyState = READYSTATE_COMPLETE; <BR>
// set the dirty flag<BR>
<BR>
m_fDirty = TRUE;<BR>
<BR>
} <BR>
// return the result<BR>
<BR>
return(hr); <BR>
}<BR>
</TT></FONT></P>
<P>Potentially, any type of data can be rendered in this fashion. The BaseCtl framework
provides a sample implementation, called WebImage, that demonstrates the rendering
of bitmap data progressively as an asynchronous property.
<H3><A NAME="Heading13"></A>Static and Dynamic Property Enumeration</H3>
<P><I>Property enumeration</I> is a way of restricting a property to a specific set
of valid values. An example of an enumeration is a property for determining the alignment
of a control's displayed text: left-justified, centered, and right-justified, in
your case. Another case is a property used to select the different languages a control
supports. Language selection properties are good candidates for both a static set,
say for English and German, and a dynamic set, say for all the languages on a particular
machine.</P>
<P>As is pointed out in <A HREF="ch07.htm">Chapters 7</A> and <A HREF="ch09.htm">9</A>,
property enumeration adds, with very little effort, a new level of sophistication
to your control implementation. <BR>
<BR>
<B>Static Property Enumeration </B><SPACER TYPE="HORIZONTAL" SIZE="10">Static Property
Enumeration for a BaseCtl implemented control is no different than your MFC and ATL
implementations. Static enumeration is dependent on the ODL and requires no control
code to implement it. See <A HREF="ch07.htm">Chapter 7</A> for the implementation
details.</P>
<P><B>Dynamic Property Enumeration </B><SPACER TYPE="HORIZONTAL" SIZE="10">As with
your MFC and ATL implementations, adding Dynamic Property Enumeration to your BaseCtl
implementation is straightforward. Unfortunately, the BaseCtl does not provide the
basic OLE interface for Dynamic Property Enumeration support that you found in MFC,
so you must add it yourself. <I>Dynamic Property Enumeration</I> is based on the
interface <TT>IPerPropertyBrowsing</TT>. You will create a macro in a style similar
to that of the BaseCtl that will provide the necessary code to implement the interface.
Listing 11.10 shows the macro and the definition that you added. Essentially, the
macro is just a collection of functions that need to be supported in order to use
a specific interface. You are not required to implement the interface with a macro
as your are doing here. The macro just makes your control class code a little bit
easier to read and manage.
<H3><A NAME="Heading14"></A>Listing 11.10 <SPACER TYPE="HORIZONTAL" SIZE="10">IPERPROPERTYBROWSING.H--IPerPropertyBrowsing
Interface Macro</H3>
<P><FONT COLOR="#0066FF"><TT>#define DECLARE_STANDARD_PERPROPERTYBROWSING() \<BR>
<BR>
STDMETHOD(MapPropertyToPage)(DISPID Dispid, LPCLSID lpclsid); \<BR>
<BR>
STDMETHOD(GetPredefinedStrings)(DISPID Dispid, CALPOLESTR* lpcaStringsOut,\ <BR>
<BR>
CADWORD* lpcaCookiesOut); \<BR>
<BR>
STDMETHOD(GetPredefinedValue)(DISPID Dispid, DWORD dwCookie, VARIANT* lpvarOut);
\ <BR>
STDMETHOD(GetDisplayString)(DISPID Dispid, BSTR* lpbstr); \<BR>
</TT></FONT></P>
<P>In order for your control to support <TT>IPerPropertyBrowsing</TT>, you need to
include the header file for your <TT>IPerPropertyBrowsing</TT> macro, inherit from
the <TT>IPerPropertyBrowsing</TT> interface, and add the macro to your class declaration
(see Listing 11.11).
<H3><A NAME="Heading15"></A>Listing 11.11<SPACER TYPE="HORIZONTAL" SIZE="10"> BCFCONTROLCTL.H--IPerPropertyBrowsing
Interface Declaration</H3>
<P><FONT COLOR="#0066FF"><TT>#include "Dispids.H"<BR>
<BR>
#include "internet.h"<BR>
<BR>
#include "IPerPropertyBrowsing.h" <BR>
#include "alignmentenums.h" <BR>
typedef struct tagBCFCONTROLCTLSTATE<BR>
<BR>
{<BR>
<BR>
long lCaptionLength;<BR>
<BR>
long lAlignment;<BR>
<BR>
OLE_COLOR ocBackColor;<BR>
<BR>
long lReadyState;<BR>
<BR>
long lTextDataPathLength;<BR>
<BR>
} BCFCONTROLCTLSTATE; <BR>
//=-------------------------------------------------------------------------=<BR>
<BR>
// CBCFControlControl<BR>
<BR>
//=-------------------------------------------------------------------------=<BR>
<BR>
// our control.<BR>
<BR>
//<BR>
<BR>
class CBCFControlControl : public CInternetControl, public IBCFControl, <BR>
<BR>
public ISupportErrorInfo, public IPerPropertyBrowsing<BR>
<BR>
{<BR>
<BR>
public:<BR>
<BR>
// IUnknown methods<BR>
<BR>
//<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -