📄 ch08.htm
字号:
{ <BR>
// NULL terminate the string reference <BR>
m_lptstrCaption = new TCHAR[1]; <BR>
m_lptstrCaption[0] = `\0'; <BR>
// set the alignment to the default of left <BR>
m_lAlignment = EALIGN_LEFT; <BR>
} <BR>
DECLARE_REGISTRY_RESOURCEID(IDR_ATLCONTROLWIN) <BR>
. . . </TT></FONT></P>
<P>The <TT>CaptionMethod</TT> contains all of the code for setting the caption and
the alignment style, and like the MFC implementation, deals with the optional parameter
correctly (see Listing 8.5). See <A HREF="ch06.htm">Chapter 6</A> for more information
about optional parameters and their use.</P>
<P>Since the <TT>CaptionMethod</TT> is used for the <TT>IDispatch</TT> implementation
and the custom interface, the method is implemented in a slightly different way than
its MFC counterpart. First the function is declared as <TT>STDMETHODIMP</TT>, which
expands to an <TT>HRESULT</TT> return type. The return value is used by OLE to determine
whether the method call succeeded. The string parameter is passed in differently
also. All strings are passed as <TT>UNICODE</TT> in OLE. This is true even for MFC.
The only difference is that MFC hides the implementation details of how the strings
are managed; the developer simply uses the appropriate string data type based on
the target application and platform, that is, Win32 ANSI versus Win32 UNICODE. Note
the use of the USES_CONVERSION and W2A macros to convert the string from UNICODE
to ANSI. <BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>Useful Helper Functions and Conversion Macros</B><BR>
The files ATLCONV.H and ATLCONV.CPP, which can be found in the directory ...\DevStudio\VC\ATL\include,
contain a number of helper functions and macros for converting data such as UNICODE
strings to ANSI. Since ATL does not require the use of MFC, you are wise to examine
these files before writing functions to convert data.
</BLOCKQUOTE>
<P>
<HR>
<BR>
<BR>
Next, if the <TT>VARIANT</TT> is of a valid data type other than <TT>VT_I4</TT>,
the method tries to convert it to a <TT>VT_I4</TT> type. You try to convert the data
for the cases where a user passes valid data in the form of a different data type,
for example, a short or a string.</P>
<P>One very important thing to note is the use of the function <TT>VariantInit:</TT>.
All <TT>VARIANT</TT> variables must be initialized prior to their use. This practice
guarantees that the <TT>VARIANT</TT> does not contain invalid data type information
or invalid values. This practice follows the basic C++ tenet of initializing all
member variables to ensure that they do not contain invalid information.</P>
<P>If the requirements of your control demand that you deal with only specific data
types, you can also choose to add code (error messages, exceptions, and so on) to
deal with the fact that the method did not receive a valid data type. If the function
<TT>VariantChangeType</TT> is unable to convert the data, the method exits and returns
a value of <TT>FALSE</TT>. A return of <TT>FALSE</TT> indicates to the caller of
the method that the method didn't succeed. Again, you can also choose to add additional
error handling code to the method to give the user more information about the error
that occurred. See <A HREF="ch03.htm">Chapters 3</A> through <A HREF="ch05.htm">5</A>
on generating OLE exceptions for more information.</P>
<P>Before proceeding, the method ensures that the <TT>m_lAlignment</TT> member variable
contains valid data.</P>
<P>If the method received valid data or converted the data to a valid value, as indicated
by the variable <TT>lResult</TT> equaling <TT>TRUE</TT>, the method stores the caption
and the alignment values in the class member variables, invalidates the control so
it will redraw its User Interface (UI) based on the new information, and exits the
function.</P>
<P>Listing 8.5 contains another important difference from that of its MFC counterpart:
use of the function <TT>FireViewChange</TT> in place of the MFC <TT>InvalidateControl</TT>
function to force the control to repaint itself. Wherever appropriate, we will point
out the differences between MFC and ATL.
<H3><A NAME="Heading9"></A>Listing 8.5 ATLCONTROLWIN.CPP--CaptionMethod Implementation</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
<BR>
STDMETHODIMP CATLControlWin::CaptionMethod(BSTR bstrCaption, VARIANT <BR>
varAlignment, long * lRetVal) <BR>
{ <BR>
// needed for the W2A macro <BR>
USES_CONVERSION; <BR>
HRESULT hResult = S_OK; <BR>
// return value initialized to failure result <BR>
*lRetVal = FALSE; <BR>
// convert the string to ANSI <BR>
LPTSTR lptstrTempCaption = W2A(bstrCaption); <BR>
// if the variant is a long just use the value <BR>
if(VT_I4 == varAlignment.vt) <BR>
{ <BR>
// assign the value to our member variable <BR>
m_lAlignment = varAlignment.lVal; <BR>
// set the return value <BR>
*lRetVal = TRUE; <BR>
} <BR>
// if the user didn't supply an alignment parameter we will assign the default <BR>
else if(VT_ERROR == varAlignment.vt || VT_EMPTY == varAlignment.vt) <BR>
{ <BR>
// assign the value to our member variable <BR>
m_lAlignment = EALIGN_LEFT; <BR>
// set the return value <BR>
*lRetVal = TRUE; <BR>
} <BR>
else <BR>
{ <BR>
// get a variant that we can use for conversion purposes <BR>
VARIANT varConvertedValue; <BR>
// initialize the variant <BR>
::VariantInit(&varConvertedValue); <BR>
// see if we can convert the data type to something useful <BR>
// VariantChangeTypeEx() could also be used <BR>
if(S_OK == ::VariantChangeType(&varConvertedValue, <BR>
(VARIANT *) &varAlignment, 0, VT_I4)) <BR>
{ <BR>
// assign the value to our member variable <BR>
switch(varConvertedValue.lVal) <BR>
{ <BR>
case EALIGN_CENTER: <BR>
m_lAlignment = EALIGN_CENTER; <BR>
break; <BR>
case EALIGN_RIGHT: <BR>
m_lAlignment = EALIGN_RIGHT; <BR>
break; <BR>
default: <BR>
m_lAlignment = EALIGN_LEFT; <BR>
break; <BR>
} <BR>
// set the return value <BR>
*lRetVal = TRUE; <BR>
} <BR>
else <BR>
{ <BR>
// at this point we could either throw an error indicating <BR>
// there was a problem converting <BR>
// the data or change the return type of the method and <BR>
// return the HRESULT value from the <BR>
// the "VariantChangeType" call. <BR>
} <BR>
} <BR>
// if everything was OK <BR>
if(TRUE == *lRetVal) <BR>
{ <BR>
// if we have a string <BR>
if(lptstrTempCaption != NULL) <BR>
{ <BR>
// if we have a string <BR>
if(m_lptstrCaption) <BR>
{ <BR>
// delete the existing string <BR>
delete [] m_lptstrCaption; <BR>
// clear the reference just to be safe <BR>
m_lptstrCaption = NULL; <BR>
} <BR>
// allocate a new string <BR>
m_lptstrCaption = new TCHAR[lstrlen(lptstrTempCaption) + 1]; <BR>
// assign the string to our member variable <BR>
lstrcpy(m_lptstrCaption, lptstrTempCaption); <BR>
} <BR>
// did they pass us bad data? <BR>
if(m_lAlignment < EALIGN_LEFT || m_lAlignment > EALIGN_RIGHT) <BR>
// sure did, lets fix their little red wagon <BR>
m_lAlignment = EALIGN_LEFT; <BR>
// force the control to repaint itself <BR>
this->FireViewChange(); <BR>
// this->InvalidateControl(); <== MFC Version <BR>
} <BR>
// return the result of the function call <BR>
return hResult; <BR>
} </TT></FONT></P>
<H2><A NAME="Heading10"></A>Properties</H2>
<P><I>Properties</I> can be categorized as user defined, stock, or ambient.</P>
<P><I>User defined properties</I> are properties that are implementation-specific
and have meaning only to the component that contains them. User defined properties
can be further broken into those properties that are defined only as their specific
data type (<I>normal</I> properties) and those with additional parameters (<I>parameterized</I>
properties).</P>
<P><I>Stock properties</I> are a set of properties that are already defined by OLE
in terms of the basic meaning. Stock properties are not implemented in the control
of the container by default. They still require implementation by the control developer.
They are predefined only to imply a certain level of uniformity between various control
implementations.</P>
<P><I>Ambient properties,</I> on the other hand, are properties that are supported
by the container to provide a default value to the control that uses them.</P>
<P>In the remainder of this section, you will create normal, parameterized, and stock
properties. You will also learn how to use ambient properties.
<H3><A NAME="Heading11"></A>Creating Normal User Defined Properties</H3>
<P>A <I>normal property</I> is a property that is declared as a single type, for
example, <TT>long</TT> or <TT>BSTR</TT>, and has no parameters. You will expose your
control's Alignment member variable through a property.</P>
<P>Properties are added in much the same way as methods. 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 (see fig. 8.11).
<B><BR>
<BR>
</B><A HREF="Art/08/gfigs11.jpg"><B>FIG. 8.11</B></A> <I><BR>
Add a new property to the control with the ATL ClassWizard.</I></P>
<P>In the Add Property to Interface dialog, set the Property <U>T</U>ype to <TT>long</TT>,
the Property <U>N</U>ame to <TT>Alignment</TT>, and leave the remainder of the settings
at their default values (see fig. 8.12). Click OK to confirm the entry and close
the dialog. <B><BR>
<BR>
</B><A HREF="Art/08/gfigs12.jpg"><B>FIG. 8.12</B></A><B> </B><I><BR>
Define the <TT>Alignment</TT> property attributes.</I></P>
<P>After adding the property, you need to update the IDL file with a dispid constant
name that can be used from within the control implementation source files. Listing
8.6 shows the <TT>typedef</TT> that is added to the IDL file. The dispids are added
as an enumeration so that the MIDL compiler will generate an enumeration in the ATLControl.h
header file, which defines the interfaces and classes available in the control IDL
file. The reason for adding the dispids as a set of constants is the same reason
for having any constant. If the value of the dispid were to change, you wouldn't
have to search your source code trying to find where you used the value. Listing
8.6 also shows the change that is made to the <TT>Alignment</TT> property declaration
functions to reflect the dispid constant.
<H3><A NAME="Heading12"></A>Listing 8.6 ATLCONTROL.IDL--Dispid Enumeration Added
to the IDL File to Aid in the Support of Properties in the Control</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
typedef enum propdispids <BR>
{ <BR>
dispidAlignment = 2, <BR>
}PROPDISPIDS; <BR>
. . . <BR>
interface IATLControlWin : IDispatch <BR>
{ <BR>
[id(1), helpstring("method CaptionMethod")] <BR>
HRESULT CaptionMethod([in] BSTR bstrCaption, <BR>
[in, optional] VARIANT varAlignment, [out, retval] long * lRetVal); <BR>
[propget, id(dispidAlignment), helpstring("property Alignment")] <BR>
HRESULT Alignment([out, retval] long *pVal); <BR>
[propput, id(dispidAlignment), helpstring("property Alignment")] <BR>
HRESULT Alignment([in] long newVal); <BR>
}; <BR>
. . . </TT></FONT></P>
<P>Open the file ATLControlWin.cpp so that you can modify the <TT>get_Alignment/put_Alignment</TT>
functions. You could also select the functions from the Class View tab of the Project
Workspace window.</P>
<P>As you can see, Listing 8.7 takes advantage of the member variable <TT>m_Alignment</TT>,
which you added earlier, and uses the member to get and set the property value.</P>
<P>The <TT>GetAlignment</TT> function is simple in that it returns only the value
stored in the <TT>m_lAlignment</TT> member variable.</P>
<P>The <TT>SetAlignment</TT> function does a little more. This function checks to
see if the value is within the valid ranges of values and, if so, stores the value
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -