📄 ch06.htm
字号:
to define the additional parameters without a method declaration. <BR>
<BR>
The <TT>dispinterface</TT> keyword is a specific convention for defining <TT>IDispatch</TT>-based
interfaces in a more C++-like fashion, as opposed to the standard <TT>IDispatch</TT>
style, which would be entered as one variable entry versus two method entries. For
more information regarding the use of <TT>dispinterface</TT> and other ODL keywords,
see the ODL documentation provided with the VC++ compiler.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0">
<H3><A NAME="Heading13"></A>Listing 6.7 MFCCONTROLWINCTL.CPP--Alignment Property
Get/Set Method Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>long CMFCControlWinCtrl::GetAlignment() <BR>
{ <BR>
// return our current setting <BR>
return m_lAlignment; <BR>
} <BR>
void CMFCControlWinCtrl::SetAlignment(long nNewValue) <BR>
{ <BR>
// if we are in the valid range for the property <BR>
if(nNewValue >= EALIGN_LEFT && nNewValue <= EALIGN_RIGHT) <BR>
{ <BR>
// set the new property value <BR>
m_lAlignment = nNewValue; <BR>
// let the control know that the property has changed <BR>
this->SetModifiedFlag(); <BR>
// refresh the property browser <BR>
this->BoundPropertyChanged(dispidAlignment); <BR>
// redraw the control <BR>
this->InvalidateControl(); <BR>
} <BR>
} </TT></FONT></P>
<P>MFC and the ClassWizard provide another option when declaring properties-- that
is, by member variable. MFC will add a member variable of the appropriate type to
the class declaration and will provide a notification function if the data changes.
The notification message is defined as <TT>Onvariable</TT><I>_</I><TT>nameChanged</TT>
and is added to the source file of the control. If you had chosen this method for
the <TT>Alignment</TT> member, the function would have been called <TT>OnAlignmentChanged</TT>
and would have been implemented in the MFCControlWinCtl.cpp file. The variable style
results in less code to write, but it also results in less flexibility because MFC
manages all of the <TT>Get/Set</TT> property code for you. Feel free to experiment
with both methods of property creation to get a feel for your best option.
<H3><A NAME="Heading14"></A>Creating Parameterized User Defined Properties</H3>
<P>A <I>parameterized property</I> is a property that, in addition to being of a
specific type (for example, <TT>BSTR</TT> or <TT>long</TT>), accepts one or more
additional parameters to further define the data of the property. Parameterized properties
can be useful for properties that represent collections of data where the additional
parameter is the index into the collection.</P>
<P>You are going to expose the controls <TT>m_cstrCaption</TT> member variable as
a parameterized property in addition to your <TT>CaptionMethod</TT> function.</P>
<P>In the Add Property dialog (see fig. 6.10), define the <U>E</U>xternal name as
<TT>CaptionProp</TT>, its <U>T</U>ype as <TT>BSTR</TT>, and its Implementation as
<TT>Get/Set</TT> <U>m</U>ethods. In addition, add a parameter called <TT>varAlignment</TT>
of type <TT>VARIANT</TT> to the <U>P</U>arameter list box. Click OK to add the property
to the class. Close the ClassWizard so you can add some code to your property. <BR>
<BR>
<A HREF="Art/06/f_fig10.jpg"><B>FIG. 6.10</B></A> <I><BR>
Add the Caption property with the Add Property dialog.</I></P>
<P>The implementation of the property <TT>CaptionProp</TT> has two methods: <TT>GetCaptionProp</TT>
and <TT>SetCaptionProp</TT>.</P>
<P><TT>GetCaptionProp</TT> is the method that is called to return data from the property.
In your implementation, you ignore the alignment parameter because it is of no use
to you in this context; you simply return the caption (see Listing 6.8). <TT>GetCaptionProp</TT>
uses the <TT>CString</TT> function <TT>AllocSysString</TT> to create a <TT>BSTR</TT>,
which is essentially a UNICODE string, that is returned from the function call.
<H3><A NAME="Heading15"></A>Listing 6.8 MFCCONTROLWINCTL.CPP--GetCaptionProp Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>BSTR CMFCControlWinCtrl::GetCaptionProp(const VARIANT
FAR& varAlignment) <BR>
{ <BR>
// return the caption as a BSTR <BR>
return m_cstrCaption.AllocSysString(); <BR>
} </TT></FONT></P>
<P><TT>SetCaptionProp</TT> simply defers to the <TT>CaptionMethod</TT> implementation
because the <TT>CaptionMethod</TT> already does everything that you need (see Listing
6.9).
<H3><A NAME="Heading16"></A>Listing 6.9 MFCCONTROLWINCTL.CPP--SetCaptionProp Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>void CMFCControlWinCtrl::SetCaptionProp(const VARIANT
FAR& varAlignment, LPCTSTR lpszNewValue) <BR>
{ <BR>
// use the "CaptionMethod" implementation <BR>
if(TRUE == this->CaptionMethod(lpszNewValue, varAlignment)) <BR>
// let the container know that the property has changed <BR>
this->SetModifiedFlag(); <BR>
} </TT></FONT></P>
<P>Your final detail is the ODL file (see Listing 6.10). You are going to make the
<TT>VARIANT</TT>, <TT>varAlignment</TT>, an optional parameter for the <TT>GetCaptionProp</TT>
and <TT>SetCaptionProp </TT>implementations. You can make the <TT>varAlignment</TT>
parameter optional for the <TT>SetCaptionProp</TT> implementation because the <TT>varAlignment</TT>
variable is the last in the list of actual parameters. The <TT>BSTR lpszNewValue</TT>
variable is actually the data type of the property function pairs and does not affect
the parameter definition rules.
<H3><A NAME="Heading17"></A>Listing 6.10 MFCCONTROL.ODL--[optional] Keyword Added
to the ODL File</H3>
<P><FONT COLOR="#0066FF"><TT>methods: <BR>
// NOTE - ClassWizard will maintain method information here. // Use extreme caution
when editing this section. <BR>
//{{AFX_ODL_METHOD(CMFCControlWinCtrl) <BR>
[id(2)] long CaptionMethod(BSTR lpctstrCaption, [optional] VARIANT varAlignment);
<BR>
[id(3), propget] BSTR CaptionProp([optional] VARIANT varAlignment); <BR>
[id(3), propput] void CaptionProp([optional] VARIANT varAlignment, BSTR lpszNewValue);
<BR>
//}}AFX_ODL_METHOD</TT></FONT></P>
<PRE><FONT COLOR="#0066FF"><TT></TT></FONT></PRE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>NOTE:</B> Because of the parameters they contain, parameterized properties
are defined in the methods section of the <TT>dispinterface</TT> declaration of an
object. All properties are implemented using the dual method style in <TT>IDispatch</TT>-based
interfaces regardless of whether the property is parameterized or not. Only <TT>dispinterfaces</TT>
contain a separate property and method section in the ODL. And only <TT>dispinterfaces</TT>
allow for properties to be declared as a single line entry in the ODL file.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0">
<H3><A NAME="Heading18"></A>Creating Stock Properties</H3>
<P>A <I>stock property</I> is a property that is understood by both a control and
its container and that has a predefined meaning to both. Stock properties are intended
to provide basic uniform functionality to all the controls and containers that implement
them. Stock properties do not require you to implement a lot of code; you just hook
into the existing property.</P>
<P>Adding stock properties is similar to adding user defined properties. Open the
ClassWizard, select the Automation tab, select the class <TT>CMFCControlWinCtrl</TT>,
and click the Add P<U>r</U>operty button. Instead of typing an <U>E</U>xternal name
this time, you select one from the list provided. Your stock property will be the
<TT>BackColor</TT> property (see fig. 6.11). Select the Stoc<U>k</U> Implementation,
and click OK to add the property to the class. <BR>
<BR>
<A HREF="Art/06/f_fig11.jpg"><B>FIG. 6.11</B></A> <BR>
<I>Add <TT>BackColor</TT> stock property to the class definition.</I></P>
<P>That's all there is to it. The <TT>BackColor</TT> property now appears in your
property list and will persist with all your other properties. Whenever you need
the value of a stock property, you can query the container for the property with
one of the stock property functions. In your case, you will use the function <TT>GetBackColor</TT>.
All of the stock properties have an associated <TT>Get/Set</TT> function pair. See
the MFC documentation for more information.</P>
<P>In addition to the default stock implementation style, you can support a member
variable implementation. This option eliminates the need to query the container for
the property but forces you to carry the value with you at all times, adding to your
instance size.</P>
<P>You can also support the <TT>Get/Set</TT> implementation method and store or use
the value in anyway you see fit, but this option also requires additional code.</P>
<P>The default stock implementation is obviously the easiest because MFC does all
the work for you.
<H3><A NAME="Heading19"></A>Using Ambient Properties</H3>
<P><I>Ambient properties</I> are properties implemented in the container in which
the control resides, as opposed to <I>stock properties, </I>which are implemented
in the control and not the container. Ambient properties share the same set of predefined
meanings and dispids as those of stock properties. A <I>dispid</I> is the unique
identifier that is used to identify the property within an interface. To use an ambient
property, the control need request only the property value from the container and
apply it in whatever manner is appropriate for the property type. The use of ambient
properties allows the control to conform to the same settings as those of the container
in which it resides. This provides much better integration between the control and
its container.</P>
<P>Take the previous example of adding the <TT>BackColor</TT> stock property to the
sample control implementation. Defined as a stock property, the user of the control
can change the background color of the control or leave it as is. If the color is
different from that of the container or if the container's background color changes
for some reason, the colors won't match, giving the appearance of a poorly integrated
and written application. However, if the control simply used the ambient background
color of its container, the control's background will always match that of the container.
The specific requirements of your control implementation will decide which route
you choose when implementing the properties your control supports.</P>
<P>To access an ambient property, you can call one of the many ambient property functions
defined in the <TT>COleContro</TT>l class, for example, <TT>AmbientBackColor()</TT>,
or use the function</P>
<P><FONT COLOR="#0066FF"><TT>this->GetAmbientProperty(DISPID_BACKCOLOR, VT_COLOR,
&varBackColor);</TT></FONT></P>
<P>to access the value. The <TT>GetAmbientProperty()</TT> function takes a dispid
as its first parameter. The dispid must be one of those defined by MFC. A complete
list of dispids can be found in the MFC source files. The second parameter is the
<TT>VARIANT</TT> data type that is being requested, and the third parameter is a
<TT>VARIANT</TT> variable into which the data is stored.
<H3><A NAME="Heading20"></A>Creating Property Sheets</H3>
<P>Property sheets are a way for a control to display its properties for review and
editing and are typically implemented in a tabbed dialog format. The original intent
of property sheets was for use in cases when a control container did not support
property browsing facilities. While property sheets have their purpose, they are
probably not necessary for all implementations.</P>
<P>Removing the property sheets from your control implementation definitely reduces
the size of your control and does not take away from its implementation.</P>
<P>Because property sheets are tabbed dialogs, most of your work will be done with
the dialog editor and the ClassWizard. Select the Resource View in the Project Workspace
window. From the list of dialogs, select <TT>IDD_PROPPAGE_MFCCONTROLWIN</TT>, and
double-click the entry to open the resource editor.</P>
<P>Using the resource editor, remove the static text control with the caption TODO,
and place a static text control and a combo box control on the dialog (see fig. 6.12).
<B><BR>
<BR>
</B><A HREF="Art/06/f_fig12.jpg"><B>FIG. 6.12</B></A> <I><BR>
Use the resource editor to add controls to the Property Sheet dialog.</I></P>
<P>Using the mouse, select the label control on the form, and click the right mouse
button. In the menu that appears, select the Properties menu item. On the General
tab, set the <U>I</U>D of the control to <TT>IDC_ALIGNMENTLABEL</TT> and set the
<U>C</U>aption to <TT>Alignment</TT>. Select the Styles tab and set the Align Te<U>x</U>t
property to <TT>Right</TT>. Close the dialog to save the information.</P>
<P>Again, using the mouse, select the combo box, use the right mouse to click on
the control, and in the menu that appears, select the Properties menu item.</P>
<P>On the General tab, set the <U>I</U>D of the control to <TT>IDC_ALIGNMENTCOMBO</TT>.</P>
<P>On the Styles tab, set the T_ype to drop-down list box, and uncheck the S<U>o</U>rt
check box. Close the dialog to save the information.</P>
<P>You have placed your two controls onto the property sheets and successfully modified
their properties. Now you need to add some code to complete the implementation. Close
the resource editor, and open the ClassWizard. Select the <TT>CMFCControlWinPropPage</TT>
class, and select the Member Variables tab. In the Control ID's combo box, select
<TT>IDC_ALIGNMENTCOMBO</TT>, and click the Add Variable button.</P>
<P>In the Add Member Variable dialog (see fig. 6.13), set the Member variable <U>n</U>ame
to <TT>m_AlignmentCombo</TT> and the <U>C</U>ategory to <TT>Control</TT>. Click OK
to confirm the information, and close the dialog. <BR>
<BR>
<A HREF="Art/06/f_fig13.jpg"><B>FIG. 6.13</B></A> <I><BR>
Add the<TT> m_AlignmentCombo</TT> member variable to the class definition.</I></P>
<P>You've added a member variable for the control. Now you add one for the data.
Click the Add Variable button again, and set the Member variable <U>n</U>ame to <TT>m_AlignmentValue</TT>
(see fig. 6.14). Set the <U>C</U>ategory to <TT>Value</TT> and the Variable <U>t</U>ype
to <TT>int</TT>. Click OK to confirm the changes, and close the dialog. <B><BR>
<BR>
</B><A HREF="Art/06/f_fig14.jpg"><B>FIG. 6.14</B></A> <I><BR>
Add the <TT>m_AlignmentValue</TT> member variable to the class definition.</I></P>
<P>Close the MFC ClassWizard dialog to confirm the new member variables. <TT>DoDataExchange</TT>
is where you are going to add the code to manage the data between the control and
the property sheet. Open the source file MFCControlWinPpg.cpp, and locate the <TT>DoDataExchange</TT>
function. Notice that the ClassWizard added two lines of code to the original implementation
of <TT>DoDataExchange</TT> (see Listing 6.11).
<H3><A NAME="Heading21"></A>Listing 6.11 MFCCONTROLWINPPG.CPP--New Member Variables
Added to the DoDataExchange Function</H3>
<P><FONT COLOR="#0066FF"><TT>void CMFCControlWinPropPage::DoDataExchange(CDataExchange*
pDX) <BR>
{ <BR>
//{{AFX_DATA_MAP(CMFCControlWinPropPage) <BR>
DDX_Control(pDX, IDC_ALIGNMENTCOMBO, m_AlignmentCombo); <BR>
DDX_CBIndex(pDX, IDC_ALIGNMENTCOMBO, m_AlignmentValue); <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -