📄 ch14.htm
字号:
<pre><font color="#008000"> // During in-place activation, </font></pre>
<pre><font color="#008000"> // CShowStringCntrItem::OnGetItemPosition</font></pre>
<pre><font color="#008000"> // will be called to determine the location of this item. </font></pre>
<pre><font color="#008000"> // The default implementation created from AppWizard simply </font></pre>
<pre><font color="#008000"> // returns a hard-coded rectangle. Usually, this rectangle </font></pre>
<pre><font color="#008000"> // would reflect the current position of the item relative </font></pre>
<pre><font color="#008000"> // to the view used for activation. You can obtain the view </font></pre>
<pre><font color="#008000"> // by calling CShowStringCntrItem::GetActiveView.</font></pre>
<pre><font color="#008000"> // TODO: return correct rectangle (in pixels) in rPosition</font></pre>
<pre><font color="#008000"> rPosition.SetRect(10, 10, 210, 210);</font></pre>
<pre><font color="#008000">}</font></pre>
<P>Like <font color="#008000">OnChange()</font>, the comments are more useful than the actual code. At the moment, the View's <font color="#008000">OnDraw()</font> function draws the contained object in a hard-coded rectangle, so this function returns
that same rectangle. You are instructed to write code that asks the active view where the object is.</P>
<p><font color="#008000">OnDeactivateUI()</font> (see Listing 14.20) is called when the object goes from being active to inactive.</P>
<P><I>Listing 14.20—CntrItem.cpp—</I>CShowStringCntrItem:: OnDeactivateUI()</P>
<pre><font color="#008000">void CShowStringCntrItem::OnDeactivateUI(BOOL bUndoable)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> COleClientItem::OnDeactivateUI(bUndoable);</font></pre>
<pre><font color="#008000"> // Hide the object if it is not an outside-in object</font></pre>
<pre><font color="#008000"> DWORD dwMisc = 0;</font></pre>
<pre><font color="#008000"> m_lpObject->GetMiscStatus(GetDrawAspect(), &dwMisc);</font></pre>
<pre><font color="#008000"> if (dwMisc & OLEMISC_INSIDEOUT)</font></pre>
<pre><font color="#008000"> DoVerb(OLEIVERB_HIDE, NULL);</font></pre>
<pre><font color="#008000">}</font></pre>
<P>While the default behavior for contained objects is outside-in, as discussed earlier, you can write <I>inside-out objects</I>. These are activated simply by moving the mouse pointer over them; clicking the object has the same effect that clicking that
region has while editing the object. For example, if the contained item is a spreadsheet, clicking might select the cell that was clicked. This can be really nice for the user, who can completely ignore the borders between the container and the contained
item, but it is harder to write.</P>
<p><font color="#008000">OnChangeItemPosition()</font> is called when the item is moved during in-place editing. It, too, contains mostly comments, as shown in Listing 14.21.</P>
<P><I>Listing 14.21—CntrItem.cpp—</I>CShowStringCntrItem:: OnChangeItemPosition()</P>
<pre><font color="#008000">BOOL CShowStringCntrItem::OnChangeItemPosition(const CRect& rectPos)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> ASSERT_VALID(this);</font></pre>
<pre><font color="#008000"> // During in-place activation </font></pre>
<pre><font color="#008000"> // CShowStringCntrItem::OnChangeItemPosition</font></pre>
<pre><font color="#008000"> // is called by the server to change the position </font></pre>
<pre><font color="#008000"> // of the in-place window. Usually, this is a result </font></pre>
<pre><font color="#008000"> // of the data in the server document changing such that </font></pre>
<pre><font color="#008000"> // the extent has changed or as a result of in-place resizing.</font></pre>
<pre><font color="#008000"> //</font></pre>
<pre><font color="#008000"> // The default here is to call the base class, which will call</font></pre>
<pre><font color="#008000"> // COleClientItem::SetItemRects to move the item</font></pre>
<pre><font color="#008000"> // to the new position.</font></pre>
<pre><font color="#008000"> if (!COleClientItem::OnChangeItemPosition(rectPos))</font></pre>
<pre><font color="#008000"> return FALSE;</font></pre>
<pre><font color="#008000"> // TODO: update any cache you may have of the item's rectangle/extent</font></pre>
<pre><font color="#008000"> return TRUE;</font></pre>
<pre><font color="#008000">}</font></pre>
<P>This code is supposed to handle moving the object, but it doesn't really. That's because <font color="#008000">OnDraw()</font> always draws the contained item in the same place. You'll fix that later.</P>
<p><font color="#008000">AssertValid()</font> and <font color="#008000">Dump()</font> are debug functions that simply call the base class functions. The last function in <font color="#008000">CShowStringCntrItem</font> is <font
color="#008000">Serialize()</font>, which is called by <font color="#008000">COleDocument:: Serialize()</font>, which in turn is called by the document's <font color="#008000">Serialize()</font>, as you've already seen. It is shown in Listing 14.22.</P>
<P><I>Listing 14.22—CntrItem.cpp—</I>CShowStringCntrItem:: Serialize()</P>
<pre><font color="#008000">void </font><font color="#008000">CShowStringCntrItem::Serialize</font><font color="#008000">(CArchive& ar)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> ASSERT_VALID(this);</font></pre>
<pre><font color="#008000"> // Call base class first to read in COleClientItem data.</font></pre>
<pre><font color="#008000"> // Because this sets up the m_pDocument pointer returned from</font></pre>
<pre><font color="#008000">// CShowStringCntrItem::GetDocument, it is a good idea to call</font></pre>
<pre><font color="#008000"> // the base class Serialize first.</font></pre>
<pre><font color="#008000"> COleClientItem::Serialize(ar);</font></pre>
<pre><font color="#008000"> // now store/retrieve data specific to CShowStringCntrItem</font></pre>
<pre><font color="#008000"> if (ar.IsStoring())</font></pre>
<pre><font color="#008000"> {</font></pre>
<pre><font color="#008000"> // TODO: add storing code here</font></pre>
<pre><font color="#008000"> }</font></pre>
<pre><font color="#008000"> else</font></pre>
<pre><font color="#008000"> {</font></pre>
<pre><font color="#008000"> // TODO: add loading code here</font></pre>
<pre><font color="#008000"> }</font></pre>
<pre><font color="#008000">}</font></pre>
<P>All this code does at the moment is call the base class function. <font color="#008000">COleDocument::Serialize()</font> stores or loads a number of counters and numbers to keep track of several different contained items, then calls helper functions
such as <font color="#008000">WriteItem()</font> or <font color="#008000">ReadItem()</font> to actually deal with the item. These functions and the helper functions they call are a bit too "behind-the-scenes" for most people, but if you'd like to
take a look at them, they are in the MFC source folder (C:\MSDEV\MFC\SRC on many installations) in the file olecli1.cpp. They do their job, which is to serialize the contained item for you.</P>
<P><A ID="I17" NAME="I17"><B>Shortcomings of this Container</B></A></P>
<P>This container application isn't ShowString yet, of course, but it has more important things wrong with it. It isn't a very good container, and that's a direct result of all those <font color="#008000">TODO</font> tasks that haven't been accomplished.
Still, the fact that it is a functioning container is a good measure of the power of the MFC classes <font color="#008000">COleDocument</font> and <font color="#008000">COleClientItem</font>. So why not build the application now and run it? After it's
running, choose <U>E</U>dit, Insert <U>N</U>ew Object and insert a bitmap image. Now that you've seen the code, it shouldn't be a surprise that Paint is immediately launched to edit the item in place, as you see in Figure 14.7.</P>
<A HREF="Pfigs07.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch14/Pfigs07.gif"><b>Fig. 14.7</b></A>
<P><I>The boilerplate container can contain items and activate them for in-place editing, like this bitmap image </I><I>being edited in Paint.</I></P>
<P>Click outside the bitmap to unselect the item and return control to the container; you see that nothing happens. Click outside the document, and again nothing happens. Are you even still in ShowString? Choose <U>F</U>ile, <U>N</U>ew, and you see that
you are. The Paint menus and toolbars go away, and a new ShowString document is created. Click the bitmap item again, and you are still editing it in Paint. How can you insert another object into the first document when the menus are those of Paint? Press
Esc to cancel in-place editing so the menus become ShowString menus again. Insert an Excel chart into the container, and the bitmap disappears as the new Excel chart is inserted, as shown in Figure 14.8. Obviously, this container leaves a lot to be
desired.</P>
<A HREF="Pfigs08.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch14/Pfigs08.gif"><b>Fig. 14.8</b></A>
<P><I>Inserting an Excel chart gets you a default chart, but it completely covers the old bitmap.</I></P>
<P>Press Esc to cancel the in-place editing, and notice that the view changes a little, as shown in Figure 14.9. That's because <font color="#008000">CShowStringView::OnDraw()</font> draws the contained item in a 200X200 pixel rectangle, so the chart has
to be squeezed a little to fit into that space. It is the server—Excel, in this case—that decides how to fit the item into the space given to it by the container.</P>
<A HREF="Pfigs09.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch14/Pfigs09.gif"><b>Fig. 14.9</b></A>
<P><I>Items can look quite different when they are not active.</I></P>
<P>As you can see, there's a lot to be done to make this feel like a real container. But first, you have to turn it back into ShowString.</P>
<P><A ID="I18" NAME="I18"><B>Returning the ShowString Functionality</B></A></P>
<P>This section provides a quick summary of the steps presented in <A HREF="index09.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index09.htm" target="text">Chapter 9</A>, "Building a Complete Application: ShowString." Open the files from the old ShowString as you go so that you can copy code and
resources wherever possible. Follow these steps:</P>
<ol>
<li><P> In ShowStringDoc.h, add the private member variables and public <font color="#008000">Get</font> functions to the class.</P>
<li><P> In <font color="#008000">CShowStringDoc::Serialize()</font>, paste the code that saves or restores these member variables. Leave the call to <font color="#008000">COleDocument::Serialize()</font> in place.</P>
<li><P> In <font color="#008000">CShowStringDoc::OnNewDocument()</font>, paste the code that initializes the member variables.</P>
<li><P> In <font color="#008000">CShowStringView::OnDraw()</font>, add the code that draws the string before the code that handles the contained items. Remove the <font color="#008000">TODO</font> task about drawing native data.</P>
<li><P> Copy the entire <U>T</U>ools menu from the old ShowString to the new container ShowString: Choose <U>F</U>ile, <U>O</U>pen to open the old ShowString.rc, open the IDR_SHOWSTTYPE menu, click the <U>T</U>ools menu, and choose <U>E</U>dit,
<U>C</U>opy. Open the new ShowString's IDR_SHOWSTTYPE menu, click the <U>W</U>indow menu, and choose <U>E</U>dit, <U>P</U>aste. Do not paste it into the IDR_SHOWSTTYPE_CNTR_IP menu.</P>
<li><P> Add the accelerator Ctrl+T for <font color="#008000">ID_TOOLS_OPTIONS</font> as described in <A HREF="index17.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index17.htm" target="text">Chapter 17</A>, "Building an ActiveX Control." Add it to the <font color="#008000">IDR_MAINFRAME</font>
accelerator only.</P>
<li><P> Delete the IDD_ABOUTBOX dialog box from the new ShowString. Copy <font color="#008000">IDD_ABOUTBOX</font> and <font color="#008000">IDD_OPTIONS</font> from the old ShowString to the new.</P>
<li><P> While IDD_OPTIONS has focus, choose <U>V</U>iew, Class <U>W</U>izard. Create the <font color="#008000">COptionsDialog</font> class as in the original ShowString.</P>
<li><P> Use the Class Wizard to connect the dialog controls to <font color="#008000">COptionsDialog</font> member variables, as described in <A HREF="index09.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index09.htm" target="text">Chapter 9</A>.</P>
<li><P> Use the Class Wizard to arrange for <font color="#008000">CShowStringDoc</font> to catch the ID_TOOLS_OPTIONS command.</P>
<li><P> In ShowStringDoc.cpp, replace the Class Wizard version of <font color="#008000">CShowStringDoc::OnToolsOptions()</font> with the <font color="#008000">OnToolsOptions()</font> from the old ShowString, which puts up the dialog box.</P>
<li><P> In ShowStringDoc.cpp, add <B>#include "OptionsDialog.h"</B> after the <font color="#008000">#include</font> statements already present.</P>
</ol>
<P>Build the application, fix any typos or other simple errors, then execute it. It should run as before, saying <font color="#008000">Hello, world!</font> in the center of the view. Convince yourself that the Options dialog box still works and that you
have restored all the old functionality. Then resize the application and the view as large as possible, so that when you insert an object it does not land on the string. Insert an Excel chart as before, and press Esc to stop editing in place. There you
have it: A version of ShowString that is also an ActiveX container. Now it's time to get to work making it a <I>good</I> container.</P>
<H3><A ID="I19" NAME="I19"><B>Moving, Resizing, and Tracking</B></A></H3>
<P>The first task you want to do, even when there is only one item contained in ShowString, is to allow the user to move and resize that item. It makes life simpler for the user if you also provide a <I>tracker </I><I>rectangle</I>, a hashed line around
the contained item. This is easy to do with the MFC class <font color="#008000">CRectTracker</font>.</P>
<P>The first step is to add a member variable to the container item (<font color="#008000">CShowStringCntrItem)</font> definition in CntrItem.h, to hold the rectangle occupied by this container item. Right-click <font
color="#008000">CShowStringCntrItem</font> in ClassView and choose Add Member Variable. The variable type is <font color="#008000">CRect</font>, the declaration is <font color="#008000">m_rect</font>; leave the access <font
color="#008000">public</font>.</P>
<P>Why <font color="#008000">public</font>? Doesn't that break encapsulation? Yes, but implementing both <font color="#008000">Set</font> and <font color="#008000">Get</font> functions (or making other classes friends) does, too. This makes the code
easier to type and read, without any real loss of information hiding.</P>
<P>This needs to be initialized in a function that is called when the container item is first used and then never again. While view classes have <font color="#008000">OnInitialUpdate()</font> and document classes have <font
color="#008000">OnNewDocument()</font>, container item classes have no such called-only-once function except the constructor. So, initialize the rectangle in the constructor, as shown in Listing 14.23.</P>
<P><I>Listing 14.23—CntrItem.cpp—</I>Constructor</P>
<pre><font color="#008000">CShowStringCntrItem::CShowStringCntrItem(CShowStringDoc* pContainer)</font></pre>
<pre><font color="#008000"> : COleClientItem(pContainer)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> m_rect = CRect(10,10,210,210); </font></pre>
<pre><font color="#008000">}</font></pre>
<P>The numerical values used here are those in the boilerplate <font color="#008000">OnDraw()</font> provided by AppWizard. Now, you need to start using the <font color="#008000">m rect</font> member variable and setting it. The functions affected are
presented in the same order as in the earlier section on <font color="#008000">CShowStringView</font>.</P>
<P>First, <font color="#008000">CShowStringView::OnDraw()</font>. Find this line:</P>
<pre><font color="#008000"> m_pSelection->Draw(pDC, CRect(10, 10, 210, 210));</font></pre>
<P>Replace it with this:</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -