📄 ch17.htm
字号:
break;
case 2:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 3:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
pdc->Ellipse(Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //center
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 4:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
pdc->Ellipse(Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //upper right
pdc->Ellipse(Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //lower left
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 5:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
pdc->Ellipse(Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //upper right
pdc->Ellipse(Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //center
pdc->Ellipse(Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //lower left
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
case 6:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //upper left
pdc->Ellipse(Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //upper right
pdc->Ellipse(Left+Xunit, Top+6*Yunit,
Left+5*Xunit, Top + 10*Yunit); //center left
pdc->Ellipse(Left+11*Xunit, Top+6*Yunit,
Left+15*Xunit, Top + 10*Yunit); //center right
pdc->Ellipse(Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //lower left
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //lower right
break;
</PRE>
<PRE> }
</PRE>
<P>Build the OCX again and try it out in the test container. You will see something
similar to Figure 17.11, which actually looks like a die!</P>
<P><A HREF="javascript:popUp('17uvc11.gif')"><B>FIG. 17.11</B></A><B> </B><I>Your
rolling-die control now looks like a die.</I></P>
<P>If you're sharp-eyed or if you stretch the die very small, you might notice that
the pattern of dots is just slightly off-center. That's because the control's height
and width are not always an exact multiple of 16. For example, if Width() returned
31, Xunit would be 1, and all the dots would be arranged between positions 0 and
16, leaving a wide blank band at the far right of the control. Luckily, the width
is typically far more than 31 pixels, and so the asymmetry is less noticeable.</P>
<P>To fix this, center the dots in the control. Find the lines that calculate Xunit
and Yunit, and then add the new lines from the code fragment in Listing 17.12.</P>
<P>
<H4>Listing 17.12  DierollCtl.cpp--Adjusting Xunit and Yunit</H4>
<PRE>//dots are 4 units wide and high, one unit from the edge
int Xunit = rcBounds.Width()/16;
int Yunit = rcBounds.Height()/16;
int Xleft = rcBounds.Width()%16;
int Yleft = rcBounds.Height()%16;
// adjust top left by amount left over
int Top = rcBounds.top + Yleft/2;
</PRE>
<PRE>int Left = rcBounds.left + Xleft/2;
</PRE>
<P>Xleft and Yleft are the leftovers in the X and Y direction. By moving Top and
Left over by half the leftover, you center the dots in the control without having
to change any other code.</P>
<P>
<H2><A NAME="Heading14"></A>Generating Property Sheets</H2>
<P>ActiveX controls have property sheets that enable the user to set properties without
any change to the container application. (Property sheets and pages are discussed
in Chapter 12, "Property Pages and Sheets.") You set these up as dialog
boxes, taking advantage of prewritten pages for font, color, and other common properties.
For this control, the obvious properties to add are the following:</P>
<UL>
<LI>A flag to indicate whether the value should be displayed as a digit or a dot
pattern
<P>
<LI>Foreground color
<P>
<LI>Background color
</UL>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> It's easy to become confused about what exactly a property page is.
Is each one of the tabs on a dialog box a separate page, or is the whole collection
of tabs a page? Each tab is called a <I>page</I> and the collection of tabs is called
a <I>sheet.</I> You set up each page as a dialog box and use ClassWizard to connect
the values on that dialog box to member variables. 
<HR>
</BLOCKQUOTE>
<H3><A NAME="Heading15"></A>Digits Versus Dots</H3>
<P>It's a simple enough matter to allow the user to choose whether to display the
current value as a digit or a dot pattern. Simply add a property that indicates this
preference and then use the property in OnDraw(). The user can set the property,
using the property page.</P>
<P>First, add the property using ClassWizard. Here's how: Bring up ClassWizard and
select the Automation tab. Make sure that the CDierollCtrl class is selected and
then click Add Property. On the Add Property dialog box, provide the external name
Dots and the internal name m_dots. The type should be BOOL because Dots can be either
TRUE or FALSE. Implement this new property as a member variable (direct-access) property.
Click OK to complete the Add Property dialog box and click OK to close ClassWizard.
The member variable is added to the class, the dispatch map is updated, and a stub
is added for the notification function, OnDotsChanged().</P>
<P>To initialize Dots and arrange for it to be saved with a document, add the following
line to DoPropExchange() after the call to PX_Short():</P>
<P>
<PRE> PX_Bool( pPX, "Dots", m_dots, TRUE);
</PRE>
<P>Initializing the Dots property to TRUE ensures that the control's default behavior
is to display the dot pattern.</P>
<P>In OnDraw(), uncomment those lines that displayed the digit. Wrap an if around
them so that the digit is displayed if m_dots is FALSE and dots are displayed if
it is TRUE. The code looks like Listing 17.13.</P>
<P>
<H4>Listing 17.13  DierollCtl.cpp--CDierollCtrl::OnDraw()</H4>
<PRE>void CDierollCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
if (!m_dots)
{
CString val; //character representation of the short value
val.Format("%i",m_number);
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 Xleft = rcBounds.Width()%16;
int Yleft = rcBounds.Height()%16;
// adjust top left by amount left over
int Top = rcBounds.top + Yleft/2;
int Left = rcBounds.left + Xleft/2;
CBrush Black;
Black.CreateSolidBrush(RGB(0x00,0x00,0x00)); //solid black brush
CBrush* savebrush = pdc->SelectObject(&Black);
switch(m_number)
{
case 1:
...
}
pdc->SelectObject(savebrush);
}
</PRE>
<PRE>}
</PRE>
<P>To give the user a way to set Dots, you build a property page by following these
steps:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Click the ResourceView tab in the Project Workspace window and then
click the + next to Dialog.
<P>
<DT></DT>
<DD><B>2. </B>The OCX has two dialog boxes: one for the About box and one for the
property page. Double-click IDD_PROPPAGE_DIEROLL to open it. Figure 17.12 shows the
boilerplate property page generated by AppWizard.
<P>
<DT></DT>
<DD><B>3. </B>Remove the static control with the TODO reminder by highlighting it
and pressing Delete.
<P>
<DT></DT>
<DD><B>4. </B>Drag a check box from the Control Palette onto the dialog box. Choose
View, Properties and then pin the Property dialog box in place.
<P>
</DL>
<P><A HREF="javascript:popUp('17uvc12.gif')"><B>FIG. 17.12</B></A><B> </B><I>AppWizard
generates an empty property page.</I></P>
<DL>
<DT><I></I></DT>
<P>
<DT><I></I></DT>
<DD>5. Change the caption to Display Dot Pattern and change the resource ID to IDC_DOTS,
as shown in Figure 17.13.
<P>
</DL>
<P><A HREF="javascript:popUp('17uvc13.gif')"><B>FIG. 17.13</B></A><B> </B><I>You
build the property page for the die-roll control like any other dialog box.</I></P>
<DL>
<DT><I></I></DT>
</DL>
<P>When the user brings up the property page and clicks to set or unset the check
box, that doesn't directly affect the value of m_dots or the Dots property. To connect
the dialog box to member variables, use ClassWizard and follow these steps:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Bring up ClassWizard while the dialog box is still open and on top,
and then select the Member Variables tab.
<P>
<DT></DT>
<DD><B>2. </B>Make sure that CDierollPropPage is the selected class and that the
IDC_DOTS resource ID is highlighted, and then click the Add Variable button.
<P>
<DT></DT>
<DD><B>3. </B>Fill in <B>m_dots</B> as the name and <B>BOOL</B> as the type, and
fill in the Optional Property Name combo box with Dots, as shown in Figure 17.14.
<P>
<DT></DT>
<DD><B>4. </B>Click OK, and ClassWizard generates code to connect the property page
with the member variables in CDierollPropPage::DoDataExchange().
<P>
</DL>
<P><A HREF="javascript:popUp('17uvc14.gif')"><B>FIG. 17.14</B></A><B> </B><I>You
connect the property page to the properties of the control with ClassWizard.</I></P>
<DL>
<DT><I></I></DT>
</DL>
<P>The path that data follows can be a little twisty. When the user brings up the
property sheet, the value of TRUE or FALSE is in a temporary variable. Clicking the
check box toggles the value of that temporary variable. When the user clicks OK,
that value goes into CDierollPropPage::m_dots and also to the Automation property
Dots. That property has already been connected to CDierollCtrl::m_dots, so the dispatch
map in CDierollCtrl will make sure that the other m_dots is changed. Because the
OnDraw() function uses CDierollCtrl::m_dots, the control's appearance changes in
response to the change made by the user on the property page. Having the same name
for the two member variables makes things more confusing to first-time control builders
but less confusing in the long run.</P>
<P>This works now. Build the control and insert it into the test container. To change
the properties, choose Edit, Dieroll Control Object, and Properties; your own property
page will appear, as shown in Figure 17.15. (The Extended tab is provided for you,
but as you can see, it doesn't really do anything. Your General tab is the important
one at the moment.) Prove to yourself that the control displays dots or a digit,
depending on the page's setting, by changing the setting, clicking OK, and then watching
the control redraw.</P>
<P>When the control is displaying the value as a number, you might want to display
that number in a font that's more in proportion with the control's current width
and height and centered within the control. That's a relatively simple modification
to OnDraw(), which you can investigate on your own.</P>
<P><A HREF="javascript:popUp('17uvc15.gif')"><B>FIG. 17.15</B></A><B> </B><I>The
control test container displays your own property page.</I><B></B></P>
<H3><A NAME="Heading16"></A>User-Selected Colors</H3>
<P>The die you've created will always have black dots on a white background, but
giving the user control to change this is remarkably simple. You need a property
for the foreground color and another for the background color. These have already
been implemented as stock properties: BackColor and ForeColor.</P>
<P><B>Stock Properties  </B>Here is the complete list of stock properties
available to a control that you write:</P>
<UL>
<LI>Appearance. Specifies the control's general look
<P>
<LI>BackColor. Specifies the control's background color
<P>
<LI>BorderStyle. Specifies either the standard border or no border
<P>
<LI>Caption. Specifies the control's caption or text
<P>
<LI>Enabled. Specifies whether the control can be used
<P>
<LI>Font. Specifies the control's default font
<P>
<LI>ForeColor. Specifies the control's foreground color
<P>
<LI>Text. Also specifies the control's caption or text
<P>
<LI>hWnd. Specifies the control's window handle
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -