📄 ch17.htm
字号:
<pre><font color="#008000"> pdc->FillRect(rcBounds, </font></pre>
<pre><font color="#008000"> CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));</font></pre>
<pre><font color="#008000"> pdc->Ellipse(rcBounds);</font></pre>
<pre><font color="#008000">}</font></pre>
<P>As discussed in the "Scrolling Windows" section of <A HREF="index06.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index06.htm" target="text">Chapter 6</A>, "Drawing On the Screen," the framework passes the function a device context to draw in, a <font color="#008000">CRect</font>
describing the space occupied by your control, and another <font color="#008000">CRect</font> describing the space that has been invalidated. The code in Listing 17.8 draws a white rectangle throughout <font color="#008000">rcBounds</font> and then draws
an ellipse inside that rectangle, using the default foreground color. Although you can keep the white rectangle for now, rather than draw an ellipse on it, draw a character that corresponds to the value in <font color="#008000">Number</font>. To do that,
replace the last line in the skeletal <font color="#008000">OnDraw()</font> with these lines:</P>
<pre><font color="#008000"> CString val; //character representation of the short value</font></pre>
<pre><font color="#008000"> val.Format("%i",m_number);</font></pre>
<pre><font color="#008000"> pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );</font></pre>
<P>These lines of code convert the <font color="#008000">short</font> value in <font color="#008000">m_number</font> (which you associated with the <font color="#008000">Number</font> property on the Add Property dialog box) to a <font
color="#008000">CString</font> variable called <font color="#008000">val</font>, using the new <font color="#008000">CString::Format()</font> function (which eliminates one of the last uses of <font color="#008000">sprintf()</font> in C++ programming). The
<font color="#008000">ExtTextOut()</font> function draws a piece of text—the character in <font color="#008000">val</font>—within the <font color="#008000">rcBounds</font> rectangle. As the die-roll control is written now, that number will always
be 3.</P>
<P>You can build and test the control right now if you would like to see how little effort it takes to make a control that does something. Unlike the other ActiveX applications, a control is not run as a stand-alone application in order to register it.
Build the project and fix any typing mistakes that you may have made. Choose <U>T</U>ools, Activ<U>e</U>X Control Test Container to bring up the control test container, shown in Figure 17.5.</P>
<A HREF="Sfigs05.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs05.gif"><b>Fig. 17.5</b></A>
<P><I>The ActiveX control test container is the ideal place to test your control.</I></P>
<blockquote><p><img src="note.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/note.gif">
<P>If the <U>T</U>ools menu in Developer Studio does not include an Activ<U>e</U>X Control Test Container item, you can add it to the menu by following these steps:</P>
<ol>
<li><P> Choose <U>T</U>ools, <U>C</U>ustomize.</P>
<li><P> Click the Tools tab.</P>
<li><P> Look over the list of tools and make sure that ActiveX Control Test Container isn't there.</P>
<li><P> Go to the bottom of the list and double-click the empty entry.</P>
<li><P> Type <B>Activ&eX Control Test Container</B> in the entry and press Enter.</P>
<li><P> Click the ... button to the right of the Command box and browse to your CD, or to the hard drive on which you installed Visual C++, and to the BIN folder beneath the Developer Studio folder. Highlight tstcon32.exe and click OK to finish browsing.
On many systems the full path will be <font color="#008000">C:\Program Files\DevStudio\VC\BIN\TSTCON32.EXE. </font>Your system may be different.</P>
<li><P> Click the rightward-pointing arrow beside the Initial Directory box and choose Target Directory from the list that appears. </P>
<li><P> Make sure that the three check boxes across the bottom of the directory are not selected.</P>
<li><P> Click the Close button.</P>
</ol>
<P>If you have not built a release version, and your target is a release version, or if you have not built a debug version and your target is a release version, you will get an error message when you choose <U>T</U>ools, Activ<U>e</U>X Control Test
Container. Simply build the control and you will be able to choose the menu item.</P>
<P>After you have installed the test container once, you will not need to do so again. By bringing up the test container from within Developer Studio like this, you make it simpler to load your die-roll control into the test container.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<P>Within the test container, choose <U>E</U>dit, <U>I</U>nsert OLE Control, and then choose Dieroll Control from the displayed list. As Figure 17.6 shows, the control appears as a white rectangle displaying a small number 3. You can move and resize this
control within the container, but that little 3 stays doggedly in the upper-left corner. The next step is to make that number change when a user clicks the die.</P>
<A HREF="Sfigs06.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs06.gif"><b>Fig. 17.6</b></A>
<P><I>By adding one property and changing two functions, you have transformed the </I><I>empty shell into a control that displays a </I><I><font color="#008000">3</font></I><I>.</I></P>
<H3><B>Reacting to a Mouse Click and Rolling the Die</B></H3>
<P>There are actually two things that you want your control to do when the user clicks the mouse on the control: to inform the container that the control has been clicked and to roll the die and display the new internal value.</P>
<P><B>Notifying the Container</B></P>
<P>Let's first tackle using an <I>event</I> to notify a container. Events are how controls notify the container of a user action. Just as there are stock properties, there are stock events. These events are already coded for you:</P>
<ul>
<li> <font color="#008000">Click</font> is coded to indicate to the container that the user clicked.</P>
<li> <font color="#008000">DblClick</font> is coded to indicate to the container that the user double-clicked.</P>
<li> <font color="#008000">Error</font> is coded to indicate an error that can't be handled by firing any other event.</P>
<li> <font color="#008000">KeyDown</font> is coded to indicate to the container that a key has gone down.</P>
<li> <font color="#008000">KeyPress</font> is coded to indicate to the container that a complete keypress (down and then up) has occurred.</P>
<li> <font color="#008000">KeyUp</font> is coded to indicate to the container that a key has gone up.</P>
<li> <font color="#008000">MouseDown</font> is coded to indicate to the container that the mouse button has gone down.</P>
<li> <font color="#008000">MouseMove</font> is coded to indicate to the container that the mouse has moved over the control.</P>
<li> <font color="#008000">MouseUp</font> is coded to indicate to the container that the mouse button has gone up.</P>
</ul>
<P>The best way to tell the container that the user has clicked over the control is to fire a <font color="#008000">Click</font> stock event. The first thing to do is to add it to the control with ClassWizard. Follow these steps:</P>
<ol>
<li><P> Bring up ClassWizard by choosing <U>V</U>iew, Class<U>W</U>izard, and click the ActiveX Events tab. Make sure that the selected class is CDierollCtrl.</P>
<li><P> Click the Add Event button and fill in the Add Event dialog box, as shown in Figure 17.7.</P>
</ol>
<A HREF="Sfigs07.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs07.gif"><b>Fig. 17.7</b></A>
<P><I>ClassWizard helps you add events to your control.</I></P>
<ol start=3>
<li><P> The external name is <font color="#008000">Click</font>; choose it from the drop-down list box and notice how the internal name is filled in as <font color="#008000">FireClick</font>.</P>
<li><P> Click OK to add the event, and your work is done.</P>
</ol>
<P>You may notice the ClassView pane has a new addition: two icons resembling handles. Click the <font color="#008000">+</font> next to <font color="#008000">DDierollEvents</font> to see that <font color="#008000">Click</font> is now listed as an event
for this application, as shown in Figure 17.8.</P>
<A HREF="Sfigs08.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs08.gif"><b>Fig. 17.8</b></A>
<P><I>ClassView displays events as well as classes.</I></P>
<P>Now when the user clicks the control, the container class will be notified. So if you are writing a backgammon game, for example, the container can respond to the click by using the new value on the die to evaluate possible moves or do some other
backgammon-specific task.</P>
<P>The second part of reacting to clicks involves actually rolling the die and redisplaying it. Not surprisingly, ClassWizard helps implement this. When the user clicks over your control, you catch it with a message map entry, just as with an ordinary
application. ClassWizard should still be up; if it is not, bring it up and follow these steps:</P>
<ol>
<li><P> Select the Message Maps tab this time and make sure that your control class, <font color="#008000">CDierollCtrl</font>, is selected in the Class Name combo box.</P>
<li><P> Scroll through the Messages list box until you find the <font color="#008000">WM_LBUTTONDOWN</font> message, which is generated by Windows whenever the left mouse button is clicked over your control.</P>
<li><P> Click <U>A</U>dd Function to add a function that will be called automatically whenever this message is generated—in other words, whenever the user clicks your control. This function must always be named <font
color="#008000">OnLButtonDown()</font>, so ClassWizard doesn't give you a dialog box asking you to confirm the name.</P>
<li><P> ClassWizard has made a skeleton version of <font color="#008000">OnLButtonDown()</font> for you; click the Edit Code button to close ClassWizard and look at the new <font color="#008000">OnLButtonDown()</font> code. Here's the skeleton:</P>
<pre><font color="#008000">void CDierollCtrl::OnLButtonDown(UINT nFlags, CPoint point)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> // TODO: Add your message handler code here and/or call default</font></pre>
<pre><font color="#008000"> </font></pre>
<pre><font color="#008000"> COleControl::OnLButtonDown(nFlags, point);</font></pre>
<pre><font color="#008000">}</font></pre>
<li><P> Replace the <font color="#008000">TODO</font> comment with a call to a new function, <font color="#008000">Roll()</font>, that you will write in the next section. This function will return a random number between 1 and 6.</P>
<pre><font color="#008000">m_number = Roll();</font></pre>
<li><P> To force a redraw, next add this line:</P>
<pre><font color="#008000">InvalidateControl();</font></pre>
<li><P> Leave the call to <font color="#008000">COleControl::OnLButtonDown()</font> at the end of the function; handles the rest of the work involved in processing the mouse click.</P>
</ol>
<P><B>Rolling the Die</B></P>
<P>To add <font color="#008000">Roll()</font> to <font color="#008000">CDierollCtrl</font>, right-click on <font color="#008000">CDierollCtrl</font> in the ClassView pane, and then choose Add Member Function from the shortcut menu that appears. As shown
in Figure 17.9, <font color="#008000">Roll()</font> should be a public function that takes no parameters and returns a <font color="#008000">short</font>.</P>
<A HREF="Sfigs09.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs09.gif"><b>Fig. 17.9</b></A>
<P><I>Use the Add Function dialog box to speed routine tasks.</I></P>
<P>What should <font color="#008000">Roll()</font> do? It should calculate a random value between 1 and 6. The C++ function that returns a random number is <font color="#008000">rand()</font>, which returns an integer between 0 and <font
color="#008000">RAND_MAX</font>. Dividing by <font color="#008000">RAND_MAX + 1</font> gives a positive number that will always be less than 1, and multiplying by 6 gives a positive number that is less than 6. The integer part of the number will be between
0 and 5, in other words. Adding 1 produces the result that you want: a number between 1 and 6. The code is shown in Listing 17.9.</P>
<P><I>Listing 17.9—DierollCtl.cpp—CDierollCtrl::Roll()</I></P>
<pre><font color="#008000">short CDierollCtrl::Roll(void)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> double number = rand();</font></pre>
<pre><font color="#008000"> number /= RAND_MAX + 1;</font></pre>
<pre><font color="#008000"> number *= 6;</font></pre>
<pre><font color="#008000"> return (short)number + 1;</font></pre>
<pre><font color="#008000">}</font></pre>
<blockquote><p><img src="note.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/note.gif">
<P>If <font color="#008000">RAND_MAX + 1</font> isn't a multiple of 6, this code will roll low numbers slightly more often than high ones. A typical value for <font color="#008000">RAND_MAX</font> is 32,767, which means that 1 and 2 will, on the average,
come up 5,462 times in 32,767 rolls. However, 3 through 6 will, on the average, come up 5,461 times. You're neglecting this inaccuracy.</P>
<P>Some die-rolling programs use the modulo function instead of this approach, but it is far less accurate. The lowest digits in the random number are least likely to be accurate. The algorithm used here produces a much more random die roll.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<P>The random number generator must be seeded before it is used, and it's traditional (and practical) to use the current time as a seed value. In <font color="#008000">DoPropExchange()</font>, add the following line before the call to <font
color="#008000">PX_Short()</font>:</P>
<pre><font color="#008000"> srand( (unsigned)time( NULL ) );</font></pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -