📄 ch06.htm
字号:
//}}AFX_DATA_MAP <BR>
DDP_PostProcessing(pDX); <BR>
} </TT></FONT></P>
<P><TT>DDX_Control</TT> is the standard MFC macro for loading a control into an MFC
class member. <TT>DDX_CBIndex</TT> is a standard MFC function for getting and setting
the current index in a combo box using the variable supplied, in this case <TT>m_AlignmentValue</TT>.
You need to change this implementation slightly to fully bind your control and property
sheet (see Listing 6.12).</P>
<P>First you load the combo box with valid property data so that the property sheet
matches the property values in the control and the property browser. The function
loads the combo box with only the valid choices if the <TT>DoDataExchange</TT> function
is being executed the first time by checking the <TT>m_bSaveAndValidate</TT> member
variable; in other words, the property dialog is not saving and validating the data
it contains.</P>
<P>To support communication between the control and the property sheet, you need
to add <TT>DDP_CBIndex</TT> before the <TT>DDX_CBIndex</TT> line. <TT>DDP_CBIndex</TT>
instructs MFC to obtain the <TT>Alignment</TT> property value from the control and
store it in the <TT>m_lAlignment</TT> member variable of the property dialog when
the dialog is loading. When the property dialog is unloading, <TT>DDP_CBIndex</TT>
retrieves the current index of the combo box and sets the <TT>Alignment</TT> property
of the control to the new value. <TT>DDP_CBIndex</TT> must be before the <TT>DDX_CBIndex</TT>
function; otherwise, the property sheet will not correctly reflect the values of
the properties in the control. <BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>NOTE:</B> Because of the specific implementation requirements of this property
dialog (that is, the list box control is being loaded into the member variable, <TT>m_AlignmentCombo</TT>,
of the class, and it is necessary to load the combo box selection list prior to setting
the current selected entry in the list), the <TT>DDP_CBIndex</TT> and <TT>DDX_CBIndex</TT>
lines had to be removed from between the MFC <TT>AFX_DATA_MAP</TT> macros to allow
the setting of the entries to occur. Separating the lines in this fashion is not
required in order to implement the <TT>DDX_CBIndex</TT> and <TT>DDP_CBIndex</TT>
lines. It is, however, the only way to solve this particular problem. <BR>
<BR>
When the <TT>DDX_CBIndex</TT> and <TT>DDPCBIndex</TT> lines are removed from between
the MFC macros, they no longer appear as a member variable in the ClassWizard and
are not managed automatically by VC++ and the ClassWizard.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0">
<H3><A NAME="Heading22"></A>Listing 6.12 MFCCONTROLWINPPG.CPP--Updated DoDataExchange
Function; Alignment Enumeration Added</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>
//}}AFX_DATA_MAP <BR>
if(!pDX->m_bSaveAndValidate) <BR>
{ <BR>
// make sure that we have cleared the list <BR>
m_AlignmentCombo.ResetContent(); <BR>
m_AlignmentCombo.AddString(_T("Left")); <BR>
m_AlignmentCombo.AddString(_T("Center")); <BR>
m_AlignmentCombo.AddString(_T("Right")); <BR>
} <BR>
DDP_CBIndex(pDX, IDC_ALIGNMENTCOMBO, m_AlignmentValue, _T("Alignment"));
<BR>
DDX_CBIndex(pDX, IDC_ALIGNMENTCOMBO, m_AlignmentValue); <BR>
DDP_PostProcessing(pDX); <BR>
this->SetModifiedFlag(); <BR>
} </TT></FONT></P>
<P>As was pointed out earlier in this chapter, in order for the property browser
within the IDE (for example, Microsoft Visual Basic's property list window) to accurately
reflect the property value after it has been changed by the Property Sheet, you must
add a <TT>BoundPropertyChanged</TT> call to your <TT>SetAlignment</TT> function (see
Listing 6.13). This action notifies the property browser that the value has changed
and that it should be retrieved again.
<H3><A NAME="Heading23"></A>Listing 6.13 MFCCONTROLWINCTL.CPP--BoundPropertyChanged
Function within the SetAlignment Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>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>The only thing remaining is to set the default value of the member variable <TT>m_AlignmentValue</TT>
to a meaningful value (see Listing 6.14). Add the include file entry</P>
<P><FONT COLOR="#0066FF"><TT>#include :MFCControlWinCtl.h"</TT></FONT></P>
<P>to the source file to resolve the <TT>EALIGN_LEFT</TT> constant. Initialize the
<TT>m_AlignmentValue</TT> member variable in the constructor to a value of <TT>EALIGN_LEFT</TT>.
<H3><A NAME="Heading24"></A>Listing 6.14 MFCCONTROLWINPPG.CPP--m_AlignmentValue Class
Member Initialized in the Class Constructor</H3>
<P><FONT COLOR="#0066FF"><TT>// MFCControlWinPpg.cpp : Implementation of the CMFCControlWinPropPage
property page class. <BR>
#include "stdafx.h" <BR>
#include "MFCControl.h" <BR>
#include "MFCControlWinCtl.h" <BR>
#include "MFCControlWinPpg.h" <BR>
. . . <BR>
CMFCControlWinPropPage::CMFCControlWinPropPage() : <BR>
COlePropertyPage(IDD, IDS_MFCCONTROLWIN_PPG_CAPTION) <BR>
{ <BR>
//{{AFX_DATA_INIT(CMFCControlWinPropPage) <BR>
m_AlignmentValue = EALIGN_LEFT; <BR>
//}}AFX_DATA_INIT <BR>
} </TT></FONT></P>
<H2><A NAME="Heading25"></A>Adding Events</H2>
<P>Properties and methods are a way for a programmer to communicate with a control
from within the container application. Events are a way for the control to communicate
with the container. For ActiveX controls, events are nothing more than <TT>IDispatch</TT>
interfaces that are implemented on the container side of the container/control relationship.
The mechanism that events are based on is known as a connection point. A <I>connection
point</I> is simply a description of the type of interface that is required in order
to communicate with the container. Connection points are not restricted to only <TT>IDispatch</TT>
interfaces, but rather they can be of any COM interface that is understood by both
components. For that matter, connection points/events are not restricted only to
controls, they can be used in any COM implementation. Controls were simply the first
to take advantage of them. For more information regarding connection points, refer
to the documentation in the OLE online help or to Kraig Brockschmidt's <I>Inside
OLE,</I> <I>Second</I> <I>Edition</I>, from Microsoft Press.</P>
<P>As with methods and properties, events are added and removed with the ClassWizard.
Open the ClassWizard, and select the ActiveX Events tab. Select the <TT>CMFCControlWinCtrl</TT>
class, and click the <U>A</U>dd Event button.</P>
<P>You can choose from a list of predefined events or add your own. You are going
to add a <TT>Change</TT> event (see fig. 6.15), and it will have two parameters.
The first parameter will be a string, passed by reference (<TT>BSTR *</TT>), called
<TT>cstrCaption</TT>. The second will be a <TT>long</TT>, also passed by reference
(<TT>long *</TT>), called <TT>lAlignment</TT>. You are passing the parameters by
reference to allow the container application the opportunity to change the values
if necessary.</P>
<P>Click OK to add the event to the class. Before you use your event, you are going
to create a common <TT>FireChange</TT> function that will encapsulate the code that
deals with the cases where the program has changed the data passed to the event.
Close the MFC ClassWizard dialog to confirm the addition of the event to the class.</P>
<P>You need to add your function prototype to the class definition in the header
file (see Listing 6.15). And you need to add the implementation to the source file
(see Listing 6.16). <B><BR>
<BR>
</B><A HREF="Art/06/f_fig15.jpg"><B>FIG. 6.15</B></A> <I><BR>
Here is where you add the <TT>Change</TT> event.</I></P>
<P>
<H3><A NAME="Heading26"></A>Listing 6.15 MFCCONTROLWINCTL.H--FireChange Function
Prototype Added to the Class Definition</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
protected: <BR>
// storage variable for the caption <BR>
CString m_cstrCaption; <BR>
// storage variable for the alignment <BR>
long m_lAlignment; <BR>
void FireChange(void); <BR>
}; </TT></FONT></P>
<H3><A NAME="Heading27"></A>Listing 6.16 MFCCONTROLWINCTL.CPP--FireChange Implementation
Added to the MFControlWinCtl.cpp Source File</H3>
<P><FONT COLOR="#0066FF"><TT>void CMFCControlWinCtrl::FireChange(void) <BR>
{ <BR>
// get a BSTR that can be passed via the event <BR>
BSTR bstrCaption = m_cstrCaption.AllocSysString(); <BR>
// fire the change event <BR>
this->FireChange(&bstrCaption, &m_lAlignment); <BR>
// let's see what the user gave us <BR>
m_cstrCaption = bstrCaption; <BR>
// free the data that was passed back <BR>
::SysFreeString(bstrCaption); <BR>
} </TT></FONT></P>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>NOTE:</B> The control is responsible for the data that is passed in to the
event, so be sure to free the <TT>BSTR</TT> that was passed in when the function
has returned to prevent a memory leak.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"><BR>
<BR>
The common <TT>FireChange</TT> function allows you to hide the details surrounding
the change event from the rest of the program. If you decide in the future to change
its implementation, it will impact only one function rather than a number of them.</P>
<P>The <TT>CaptionMethod</TT> will require that you fire a Change event if the data
changes, so you will add your new event there (see Listing 6.17). You also want to
add it to the <TT>SetAlignment</TT> function, but do not add <TT>FireChange</TT>
to the <TT>SetCaptionProp</TT> function because it defers to the <TT>CaptionMethod</TT>
function. Also, do not forget to add the function to any new functions or features
that are added to the control as its implementation progresses.
<H3><A NAME="Heading28"></A>Listing 6.17 MFCCONTROWINCTL.CPP--FireChange Event Added
to the CaptionMethod Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
// if everything was OK <BR>
if(TRUE == lResult) <BR>
{ <BR>
// if we have a string <BR>
if(lpctstrCaption != NULL) <BR>
// assign the string to our member variable <BR>
m_cstrCaption = lpctstrCaption; <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>
// fire the global change event <BR>
this->FireChange(); <BR>
// force the control to repaint itself <BR>
this->InvalidateControl(); <BR>
} <BR>
// return the result of the function call <BR>
return lResult; <BR>
} </TT></FONT></P>
<H2><A NAME="Heading29"></A>Persistence</H2>
<P><I>Persistence</I> refers to the capability of a component to retain its state
across execution lifetimes. In other words, regardless of the number of times that
the control is started and stopped, it remembers that you changed its background
color from white to mauve (even if it finds the color mauve revolting).</P>
<P>The implementation of persistence, for MFC controls, is a no-brainer consisting
of very little code. Simply add a single line to the <TT>DoPropExchange</TT> method
of your control for each one of your properties, and you are finished. You can get
a little more fancy and persist the properties differently, depending on whether
you are saving or loading the properties, but that isn't real complicated either.</P>
<P>Now modify your <TT>DoPropExchange</TT> function to persist the two properties,
<TT>m_lAlignment</TT> and <TT>m_cstrCaption</TT>. The persistence can be implemented
two ways. The first, and the simplest, is to add one entry for each property without
regard for order or dependencies. <I>Dependencies</I> refers to the fact that as
your control implementation gets more complicated or requires a larger number of
properties, you may find that how or when certain properties are persisted can depend
on other properties. <BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -