📄 ch17.htm
字号:
</UL>
<P><B>Ambient Properties  </B>Controls can also access <I>ambient properties,</I>
which are properties of the environment that surrounds the control--that is, properties
of the container into which you place the control. You can't change ambient properties,
but the control can use them to adjust its own properties. For example, the control
can set its background color to match that of the container.</P>
<P>The container provides all support for ambient properties. Any of your code that
uses an ambient property should be prepared to use a default value if the container
doesn't support that property. Here's how to use an ambient property called UserMode:</P>
<P>
<PRE>BOOL bUserMode;
if( !GetAmbientProperty( DISPID_AMBIENT_USERMODE,
VT_BOOL, &bUserMode ) )
{
bUserMode = TRUE;
}
</PRE>
<P>This code calls GetAmbientProperty() with the display ID (DISPID) and variable
type (vartype) required. It also provides a pointer to a variable into which the
value is placed. This variable's type must match the vartype. If GetAmbientProperty()
returns FALSE, bUserMode is set to a default value.</P>
<P>A number of useful DISPIDs are defined in olectl.h, including these:</P>
<P>DISPID_AMBIENT_BACKCOLOR</P>
<P>DISPID_AMBIENT_DISPLAYNAME</P>
<P>DISPID_AMBIENT_FONT</P>
<P>DISPID_AMBIENT_FORECOLOR</P>
<P>DISPID_AMBIENT_LOCALEID</P>
<P>DISPID_AMBIENT_MESSAGEREFLECT</P>
<P>DISPID_AMBIENT_SCALEUNITS</P>
<P>DISPID_AMBIENT_TEXTALIGN</P>
<P>DISPID_AMBIENT_USERMODE</P>
<P>DISPID_AMBIENT_UIDEAD</P>
<P>DISPID_AMBIENT_SHOWGRABHANDLES</P>
<P>DISPID_AMBIENT_SHOWHATCHING</P>
<P>DISPID_AMBIENT_DISPLAYASDEFAULT</P>
<P>DISPID_AMBIENT_SUPPORTSMNEMONICS</P>
<P>DISPID_AMBIENT_AUTOCLIP</P>
<P>DISPID_AMBIENT_APPEARANCE</P>
<P>Remember that not all containers support all these properties. Some might not
support any, and still others might support properties not included in the preceding
list.</P>
<P>The vartypes include those shown in Table 17.1.</P>
<P>
<H4>Table 17.1  Variable Types for Ambient Properties</H4>
<P>
<TABLE BORDER="1">
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">vartype</TD>
<TD ALIGN="LEFT"><B>Description</B></TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_BOOL</TD>
<TD ALIGN="LEFT">BOOL</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_BSTR</TD>
<TD ALIGN="LEFT">CString</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_I2</TD>
<TD ALIGN="LEFT">short</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_I4</TD>
<TD ALIGN="LEFT">long</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_R4</TD>
<TD ALIGN="LEFT">float</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_R8</TD>
<TD ALIGN="LEFT">double</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_CY</TD>
<TD ALIGN="LEFT">CY</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_COLOR</TD>
<TD ALIGN="LEFT">OLE_COLOR</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_DISPATCH</TD>
<TD ALIGN="LEFT">LPDISPATCH</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">VT_FONT</TD>
<TD ALIGN="LEFT">LPFONTDISP</TD>
</TR>
</TABLE>
</P>
<P>Remembering which vartype goes with which DISPID and checking the return from
GetAmbientProperty() are a bothersome process, so the framework provides member functions
of COleControl to get the most popular ambient properties:</P>
<UL>
<LI>OLE_COLOR AmbientBackColor()
<P>
<LI>CString AmbientDisplayName()
<P>
<LI>LPFONTDISP AmbientFont() (Don't forget to release the font by using Release().)
<P>
<LI>OLE_COLOR AmbientForeColor()
<P>
<LI>LCID AmbientLocaleID()
<P>
<LI>CString AmbientScaleUnits()
<P>
<LI>short AmbientTextAlign() (0 means general--numbers right, text left; 1 means
left-justify; 2 means center; and 3 means right-justify.)
<P>
<LI>BOOL AmbientUserMode() (TRUE means user mode; FALSE means design mode.)
<P>
<LI>BOOL AmbientUIDead()
<P>
<LI>BOOL AmbientShowHatching()
<P>
<LI>BOOL AmbientShowGrabHandles()
</UL>
<P>All these functions assign reasonable defaults if the container doesn't support
the requested property.</P>
<P><B>Implementing <I>BackColor</I> and <I>ForeColor  </I></B>To add BackColor
and ForeColor to the control, follow these steps:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Bring up ClassWizard, and select the Automation tab.
<P>
<DT></DT>
<DD><B>2. </B>Make sure that CDierollCtrl is the selected class, and click Add Property.
<P>
<DT></DT>
<DD><B>3. </B>Choose BackColor from the top combo box, and the rest of the dialog
box is filled out for you; it is grayed out to remind you that you can't set any
of these fields for a stock property. Figure 17.16 shows the values provided for
you.
<P>
</DL>
<P><A HREF="javascript:popUp('17uvc16.gif')"><B>FIG. 17.16</B></A><B> </B><I>ClassWizard
describes stock properties for you.</I></P>
<DL>
<DT><I></I></DT>
<P>
<DT><I></I></DT>
<DD><B>4. </B>Click OK and then add ForeColor in the same way. After you click OK,
ClassWizard's Automation tab will resemble Figure 17.17. The S next to these new
properties reminds you that they are stock properties.
<P>
<DT></DT>
<DD><B>5. </B>Click OK to close ClassWizard.
<P>
</DL>
<P><A HREF="javascript:popUp('17uvc17.gif')"><B>FIG. 17.17</B></A><B> </B><I>An S
precedes the stock properties in the OLE Automation list of properties and methods.</I></P>
<DL>
<DT><I></I></DT>
<P>
</DL>
<P>Setting up the property pages for these colors is almost as simple because there
is a prewritten page that you can use. Look through DierollCtl.cpp for a block of
code like Listing 17.14.</P>
<P>
<H4>Listing 17.14  DierollCtl.cpp--Property Pages</H4>
<PRE>//////////////////////////////////////////////////////////////
// Property pages
// TODO: Add more property pages as needed. Remember to increase the count!
BEGIN_PROPPAGEIDS(CDierollCtrl, 1)
PROPPAGEID(CDierollPropPage::guid)
</PRE>
<PRE>END_PROPPAGEIDS(CDierollCtrl)
</PRE>
<P>Remove the TODO reminder, change the count to 2, and add another PROPPAGEID so
that the block looks like Listing 17.15.</P>
<P>
<H4>Listing 17.15  DierollCtl.cpp--Property Pages</H4>
<PRE>/////////////////////////////////////////////////////////////////////////////
// Property pages
BEGIN_PROPPAGEIDS(CDierollCtrl, 2)
PROPPAGEID(CDierollPropPage::guid)
PROPPAGEID(CLSID_CColorPropPage)
</PRE>
<PRE>END_PROPPAGEIDS(CDierollCtrl)
</PRE>
<P>CLSID_CColorPropPage is a class ID for a property page that is used to set colors.
Now when the user brings up the property sheet, there will be two property pages:
one to set colors and the general page that you already created. Both ForeColor and
BackColor will be available on this page, so all that remains to be done is using
the values set by the user. You will have a chance to see that very soon, but first,
your code needs to use these colors.</P>
<P><B>Changes to <I>OnDraw()  </I></B>In OnDraw(), your code can access
the background color with GetBackColor(). Though you can't see it, this function
was added by ClassWizard when you added the stock property. The dispatch map for
CDierollCtrl now looks like Listing 17.16.</P>
<P>
<H4>Listing 17.16  DierollCtl.cpp--Dispatch Map</H4>
<PRE>BEGIN_DISPATCH_MAP(CDierollCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CDierollCtrl)
DISP_PROPERTY_NOTIFY(CDierollCtrl, "Number", m_number,
[ccc] OnNumberChanged, VT_I2)
DISP_PROPERTY_NOTIFY(CDierollCtrl, "Dots", m_dots,
[ccc] OnDotsChanged, VT_BOOL)
DISP_STOCKPROP_BACKCOLOR()
DISP_STOCKPROP_FORECOLOR()
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CDierollCtrl, "AboutBox",
[ccc]DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
</PRE>
<PRE>END_DISPATCH_MAP()
</PRE>
<P>The macro DISP_STOCKPROP_BACKCOLOR() expands to these lines:</P>
<P>
<PRE>#define DISP_STOCKPROP_BACKCOLOR() \
DISP_PROPERTY_STOCK(COleControl, "BackColor", \
DISPID_BACKCOLOR, COleControl::GetBackColor, \
COleControl::SetBackColor, VT_COLOR)
</PRE>
<P>This code is calling another macro, DISP_PROPERTY_STOCK, which ends up declaring
the GetBackColor() function as a member of CDierollCtrl, which inherits from COleControl.
Although you can't see it, this function is available to you. It returns an OLE_COLOR,
which you translate to a COLORREF with TranslateColor(). You can pass this COLORREF
to CreateSolidBrush() and use that brush to paint the background. Access the foreground
color with GetForeColor() and give it the same treatment. (Use SetTextColor() in
the digit part of the code.) Listing 17.17 shows the completed OnDraw() (with most
of the switch statement cropped out).</P>
<P>
<H4>Listing 17.17  DierollCtl.cpp--CDierollCtrl::OnDraw()</H4>
<PRE>void CDierollCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,
const CRect& rcInvalid)
{
COLORREF back = TranslateColor(GetBackColor());
CBrush backbrush;
backbrush.CreateSolidBrush(back);
pdc->FillRect(rcBounds, &backbrush);
if (!m_dots)
{
CString val; //character representation of the short value
val.Format("%i",m_number);
pdc->SetTextColor(TranslateColor(GetForeColor()));
pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );
}
else
{
//dots are 4 units wide and high, one unit from the edge
int Xunit = rcBounds.Width()/16;
int Yunit = rcBounds.Height()/16;
int Top = rcBounds.top;
int Left = rcBounds.left;
COLORREF fore = TranslateColor(GetForeColor());
CBrush forebrush;
forebrush.CreateSolidBrush(fore);
CBrush* savebrush = pdc->SelectObject(&forebrush);
switch(m_number)
{
...
}
pdc->SelectObject(savebrush);
}
</PRE>
<PRE>}
</PRE>
<P>Build the control again, insert it into the test container, and again bring up
the property sheet by choosing Edit, Dieroll Control Object, Properties. As Figure
17.18 shows, the new property page is just fine for setting colors. Change the foreground
and background colors a few times and experiment with both dots and digit display
to exercise all your new code.</P>
<P><A HREF="javascript:popUp('17uvc18.gif')"><B>FIG. 17.18</B></A><B> </B><I>Stock
property pages make short work of letting the user set colors.</I><B></B></P>
<H2><A NAME="Heading17"></A>Rolling on Demand</H2>
<P>ActiveX controls expose methods (functions) just as Automation servers do. This
control rolls when the user clicks it, but you might want the container application
to request a roll without the user's intervention. To do this, you add a function
called DoRoll() and expose it.</P>
<P>Bring up ClassWizard, click the Automation tab, and then click Add Method. Name
the new function <B>DoRoll</B>, select Return Type of Void, and when it is added,
click Edit Code and fill it in like this:</P>
<P>
<PRE>void CDierollCtrl::DoRoll()
{
m_number = Roll();
InvalidateControl();
}
</PRE>
<
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -