📄 ch18.htm
字号:
</PRE>
<P>You can then include variations on this position, mostly by multiplying the center
of the quadrant by 3 to move it to the center of the right or lower quadrant, and
you can calculate the positions of the other three spinners.</P>
<P>Once you calculate the length and position for the spinner, you call the SetLength
and SetPoint functions to pass these values to the spinner that they have been calculated
for:</P>
<P>
<PRE>pSpin->SetLength(iLength);
pSpin->SetPoint(pPos);
</PRE>
<H4>Initializing the Spinners</H4>
<P>Because you wrote the previous function to calculate the location of each spinner
on the window to work on only one spinner each time it is called, you need some routine
that will initialize each spinner, calling the previous function once for each spinner.
You need this function to get a pointer to the view object and pass that along to
the spinner. You also need to get pointers to the check box variables for the spinners
that will be used by the independently running threads. Your code can do all this
by just looping through the array of spinners, setting both of these pointers for
each spinner, and then passing the spinner to the function you just finished.</P>
<P>To create this function for your application, add a new member function to the
document class (CTaskingDoc in this instance). Specify the type as void, and give
the function a suitable name (for instance, InitSpinners), and then specify the access
as private because you'll only need to call this function once when the application
is starting. Edit the new function, adding the code in Listing 18.8.</P>
<P>
<H4>LISTING 18.8. THE CTaskingDoc InitSpinners FUNCTION.</H4>
<PRE> 1: void CTaskingDoc::InitSpinners()
2: {
3: int i;
4:
5: // Get the position of the view
6: POSITION pos = GetFirstViewPosition();
7: // Did we get a valid position?
8: if (pos != NULL)
9: {
10: // Get a pointer to the view
11: CView* pView = GetNextView(pos);
12:
13: // Loop through the spinners
14: for (i = 0; i < 4; i++)
15: {
16: // Set the pointer to the view
17: m_cSpin[i].SetViewWnd(pView);
18: // Initialize the pointer to the continuation indicator
19: m_cSpin[i].SetContinue(NULL);
20: switch (i)
21: {
22: case 1: // Set the pointer to the first thread
23: // continuation indicator
24: m_cSpin[i].SetContinue(&((CTaskingView*)pView)- Â>m_bThread1);
25: break;
26: case 3: // Set the pointer to the second thread
27: // continuation indicator
28: m_cSpin[i].SetContinue(&((CTaskingView*)pView)- Â>m_bThread2);
29: break;
30: }
31: // Calculate the location of the spinner
32: CalcPoint(i, &m_cSpin[i]);
33: }
34: }
35: }
</PRE>
<P>In this function, you first went through the steps of getting a pointer to the
view class from the document, as you did initially back on Day 10. Once you have
a valid pointer to the view, start a loop to initialize each of the spinners in the
array. You call the SetViewWnd spinner function to set the spinner's pointer to the
view window and then initialize the spinner's pointer to the check box variable to
NULL for all spinners. If the spinner is either of the two that will be used by independent
threads, you pass a pointer to the appropriate check box variable. Once you set all
of this, call the CalcPoint function that you created just a few minutes earlier
to calculate the location of the spinner on the view window.</P>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>NOTE:</STRONG> Although you've seen several examples of using pointers, the way
that you are passing a pointer to the check box variable to the spinner deserves
taking a closer look:
<PRE>m_cSpin[i].SetContinue(&((CTaskingView*)pView)->m_bThread1);</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<BLOCKQUOTE>
<P>In this statement, you take the pointer to the view object, pView, which is a
pointer for a CView object, and cast it as a pointer to the specific view class that
you have created in your application:
<PRE>(CTaskingView*)pView</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<BLOCKQUOTE>
<P>Now that you can treat the pointer to the view object as a CTaskingView object,
you can get to the check box variable, m_bThread1, which is a public member of the
CTaskingView class:
<PRE>((CTaskingView*)pView)->m_bThread1</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<BLOCKQUOTE>
<P>Once you access the m_bThread1 variable, get the address of this variable by placing
an ampersand in front of this whole string:
<PRE>&((CTaskingView*)pView)->m_bThread1</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<BLOCKQUOTE>
<P>Passing this address for the m_bThread1 variable to the SetContinue function,
you are, in effect, passing a pointer to the m_bThread1 variable, which can be used
to set the pointer to this variable that the spinner object contains.
<HR>
</BLOCKQUOTE>
<P>Now that you've created the routines to initialize all the spinners, make sure
that this routine is called when the application is started. The best place to put
this logic is the OnNewDocument function in the document class. This function will
be called when the application is started, so it is a logical place to trigger the
initialization of the spinner objects. To add this code to the OnNewDocument function,
add the code in Listing 18.9 to the OnNewDocument function in the document class.</P>
<P>
<PRE><B>Listing 18.9. </B>The CTASKINGDOC ONNEWDOCUMENT function.
1: BOOL CTaskingDoc::OnNewDocument()
2: {
3: if (!CDocument::OnNewDocument())
4: return FALSE;
5:
6: // TODO: add reinitialization code here
7: // (SDI documents will reuse this document)
8:
9: ///////////////////////
10: // MY CODE STARTS HERE
11: ///////////////////////
12:
13: // Initialize the spinners
14: InitSpinners();
15:
16: ///////////////////////
17: // MY CODE ENDS HERE
18: ///////////////////////
19:
20: return TRUE;
21: }
</PRE>
<H4>Spinning the Spinner</H4>
<P>Once last thing that you'll add to the document class for now is a means of calling
the Draw function for a specific spinner from outside the document class. Because
the array of spinners was declared as a private variable, no outside objects can
get access to the spinners, so you need to add access for the outside objects. You
can add a function to provide this access by adding a new member function to your
document class. Specify the function type as void, specify the function declaration
with a name and a single integer argument for the spinner number, such as DoSpin(int
nIndex), and then specify the function's access as public. Once you have added the
function, you can add the code in Listing 18.10 to the function to perform the actual
call to the specified spinner.</P>
<P>
<H4>LISTING 18.10. THE CTaskingDoc DoSpin FUNCTION.</H4>
<PRE>1: void CTaskingDoc::DoSpin(int nIndex)
2: {
3: // Spin the Spinner
4: m_cSpin[nIndex].Draw();
5: }
</PRE>
<H3><A NAME="Heading9"></A>Adding the OnIdle Tasks</H3>
<P>Now that you have the supporting functionality in place, it's time to turn your
attention to adding the various threads that will turn the various spinners. The
first threads to add are the ones executing while the application is idle. You'll
add a clicked event handler for the two On Idle check boxes so that you can keep
the variables for these two check boxes in sync with the window. You'll also add
the code to the application's OnIdle function to run these two spinners when the
application is idle and the check boxes for these two spinner threads are checked.</P>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>NOTE:</STRONG> The use of the term <I>thread</I> in the preceding is slightly misleading.
Any functionality that you place in the OnIdle function is running in the main application
thread. All the OnIdle processing that you add to the sample application won't be
running as an independent thread, but will be just functions that can be called from
the main thread.
<HR>
</BLOCKQUOTE>
<H4>Starting and Stopping the OnIdle Tasks</H4>
<P>The OnIdle function will check the values of the two check box variables that
specify whether each should run, so all your application needs to do when either
of these check boxes is clicked is make sure that the variables in the view object
are synchronized with the controls on the window. All that you need to do to accomplish
this is call the UpdateData function when either of these controls is clicked. You
need to be able to start and stop the OnIdle tasks by adding a single event handler
for both of the On Idle Thread check boxes and then calling the UpdateData function
in this event function.</P>
<P>To add this to your application, open the Class Wizard and select the view class
(in this case, CTaskingView). Select one of the On Idle check boxes and add a function
for the BN_CLICKED event. Change the name of the suggested function to OnCbonidle
and click OK. Do the same thing for the other On Idle check box. Once you specify
that both of these events use the same code, click on the Edit Code button and add
the code in Listing 18.11.</P>
<P>
<H4>LISTING 18.11. THE CTaskingView OnCbonidle FUNCTION.</H4>
<PRE> 1: void CTaskingView::OnCbonidle()
2: {
3: // TODO: Add your control notification handler code here
4:
5: ///////////////////////
6: // MY CODE STARTS HERE
7: ///////////////////////
8:
9: // Sync the variables with the dialog
10: UpdateData(TRUE);
11:
12: ///////////////////////
13: // MY CODE ENDS HERE
14: ///////////////////////
15: }
</PRE>
<H4>Building the OnIdle Threads</H4>
<P>If you examine the application class (CTaskingApp) source code, you'll find that
the OnIdle function isn't there. All the functionality that the OnIdle function needs
to perform by default is in the ancestor class of the application class that was
created for your project. The only reason to have an OnIdle function in your application
class is that your application needs some specific functionality to be performed
during this event. As a result, you need to specifically add this event handler to
your application using the Class Wizard.</P>
<P>Once you add the OnIdle function to your application class, what does it need
to do? First, it needs to get a pointer to the view so that it can check the status
of the check box variables. Next, it needs to get a pointer to the document class
so that it can call the DoSpin function to trigger the appropriate spinner object.
The key to both of these actions is getting pointers to each of these objects. When
you begin looking at what is necessary to get these pointers, you'll find that you
have to reverse the order in which you get the pointers. You need to get a pointer
to the document object in order to get a pointer to the view. However, to get a pointer
to the document, you have to go through the document template, getting a pointer
to the template before you can get a pointer to the document. Each of these steps
requires the same sequence of events, first getting the position of the first object
and then getting a pointer to the object in that position. What you'll do is get
the position of the first document template and then get a pointer to the document
template in that position. Next, you'll use the document template to get the position
of the first document and then use the document template to get a pointer to the
document in that first position. Finally, you'll use the document to get the position
of the first view and then use the document again to get a pointer to the view in
the position specified. Once you've got a pointer to the view, you can check the
value of the check boxes and call the appropriate spinner.</P>
<P>To add this functionality to your application, use the Class Wizard to add a function
to the OnIdle event message for the application class (in this case, CTaskingApp).
Once you add the function, click the Edit Code button and add the code in Listing
18.12.</P>
<P>
<H4>LISTING 18.12. THE CTaskingApp OnIdle FUNCTION.</H4>
<PRE> 1: BOOL CTaskingApp::OnIdle(LONG lCount)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -