📄 ch14.htm
字号:
{
m_rect = CRect(10,10,210,210);
</PRE>
<PRE>}
</PRE>
<P>The numerical values used here are those in the boilerplate OnDraw() provided
by AppWizard. Now you need to start using the m rect member variable and setting
it. The functions affected are presented in the same order as in the earlier section,
CShowStringView.</P>
<P>First, change CShowStringView::OnDraw(). Find this line:</P>
<P>
<PRE> m_pSelection->Draw(pDC, CRect(10, 10, 210, 210));
</PRE>
<P>Replace it with this:</P>
<P>
<PRE> m_pSelection->Draw(pDC, m_pSelection->m_rect);
</PRE>
<P>Next, change CShowStringCntrItem::OnGetItemPosition(), which needs to return this
rectangle. Take away all the comments and the old hardcoded rectangle (leave the
ASSERT_VALID macro call), and add this line:</P>
<P>
<PRE> rPosition = m_rect;
</PRE>
<P>The partner function</P>
<P>
<PRE>CShowStringCntrItem::OnChangeItemPosition()
</PRE>
<P>is called when the user moves the item. This is where m_rect is changed from the
initial value. Remove the comments and add code immediately after the call to the
base class function, COleClientItem::OnChangeItemPosition(). The code to add is:</P>
<P>
<PRE> m_rect = rectPos;
GetDocument()->SetModifiedFlag();
GetDocument()->UpdateAllViews(NULL);
</PRE>
<P>Finally, the new member variable needs to be incorporated into CShowStringCntrItem::Serialize().
Remove the comments and add lines in the storing and saving blocks so that the function
looks like Listing 14.24.</P>
<P>
<H4>Listing 14.24  CntrItem.cpp--CShowStringCntrItem::Serialize()</H4>
<PRE>void CShowStringCntrItem::Serialize(CArchive& ar)
{
ASSERT_VALID(this);
// Call base class first to read in COleClientItem data.
// Because this sets up the m_pDocument pointer returned from
// CShowStringCntrItem::GetDocument, it is a good idea to call
// the base class Serialize first.
COleClientItem::Serialize(ar);
// now store/retrieve data specific to CShowStringCntrItem
if (ar.IsStoring())
{
ar << m_rect;
}
else
{
ar >> m_rect;
}
</PRE>
<PRE>}
</PRE>
<P>Build and execute the application, insert a bitmap, and scribble something in
it. Press Esc to cancel editing in place, and your scribble shows up in the top-right
corner, next to Hello, world!. Choose Edit, Bitmap Image Object and then Edit. (Choosing
Open allows you to edit it in a different window.) Use the resizing handles that
appear to drag the image over to the left, and then press Esc to cancel in-place
editing. The image is drawn at the new position, as expected.</P>
<P>Now for the tracker rectangle. The Microsoft tutorials recommend writing a helper
function, SetupTracker(), to handle this. Add these lines to CShowStringView::OnDraw(),
just after the call to m_pSelection->Draw():</P>
<P>
<PRE> CRectTracker trackrect;
SetupTracker(m_pSelection,&trackrect);
trackrect.Draw(pDC);
</PRE>
<BLOCKQUOTE>
<P>
<HR>
<strong>CAUTION:</strong><B> </B>The one-line statement after the if was not in brace brackets
before; don't forget to add them. The entire if statement should look like this:
<HR>
</BLOCKQUOTE>
<PRE>if (m_pSelection != NULL)
{
m_pSelection->Draw(pDC, m_pSelection->m_rect);
CRectTracker trackrect;
SetupTracker(m_pSelection,&trackrect);
trackrect.Draw(pDC);
}
</PRE>
<P>Add the following public function to ShowStringView.h (inside the class definition):</P>
<P>
<PRE> void SetupTracker(CShowStringCntrItem* item,
CRectTracker* track);
</PRE>
<P>Add the code in Listing 14.25 to ShowStringView.cpp immediately after the destructor.</P>
<P>
<H4>Listing 14.25  ShowStringView.cpp--CShowStringView::SetupTracker()</H4>
<PRE>void CShowStringView::SetupTracker(CShowStringCntrItem* item,
CRectTracker* track)
{
track->m_rect = item->m_rect;
if (item == m_pSelection)
{
track->m_nStyle |= CRectTracker::resizeInside;
}
if (item->GetType() == OT_LINK)
{
track->m_nStyle |= CRectTracker::dottedLine;
}
else
{
track->m_nStyle |= CRectTracker::solidLine;
}
if (item->GetItemState() == COleClientItem::openState ||
item->GetItemState() == COleClientItem::activeUIState)
{
track->m_nStyle |= CRectTracker::hatchInside;
}
</PRE>
<PRE>}
</PRE>
<P>This code first sets the tracker rectangle to the container item rectangle. Then
it adds styles to the tracker. The styles available are as follows:</P>
<UL>
<LI>solidLine--Used for an embedded item.
<P>
<LI>dottedLine--Used for a linked item.
<P>
<LI>hatchedBorder--Used for an in-place active item.
<P>
<LI>resizeInside--Used for a selected item.
<P>
<LI>resizeOutside--Used for a selected item.
<P>
<LI>hatchInside--Used for an item whose server is open.
</UL>
<P>This code first compares the pointers to this item and the current selection.
If they are the same, this item is selected and it gets resize handles. It's up to
you whether these handles go on the inside or the outside. Then this code asks the
item whether it is linked (dotted line) or not (solid line.) Finally, it adds hatching
to active items.</P>
<P>Build and execute the application, and try it out. You still cannot edit the contained
item by double-clicking it; choose Edit from the cascading menu added at the bottom
of the Edit menu. You can't move and resize an inactive object, but if you activate
it, you can resize it while active. Also, when you press Esc, the inactive object
is drawn at its new position.</P>
<P>
<H2><A NAME="Heading5"></A>Handling Multiple Objects and Object Selection</H2>
<P>The next step is to catch mouse clicks and double-clicks so that the item can
be resized, moved, and activated more easily. This involves testing to see whether
a click is on a contained item.</P>
<P>
<H3><A NAME="Heading6"></A>Hit Testing</H3>
<P>You need to write a helper function that returns a pointer to the contained item
that the user clicked, or NULL if the user clicked an area of the view that has no
contained item. This function runs through all the items contained in the document.
Add the code in Listing 14.26 to ShowStringView.cpp immediately after the destructor.</P>
<P>
<H4>Listing 14.26  ShowStringView.cpp--CShowStringView::SetupTracker()</H4>
<PRE>CShowStringCntrItem* CShowStringView::HitTest(CPoint point)
{
CShowStringDoc* pDoc = GetDocument();
CShowStringCntrItem* pHitItem = NULL;
POSITION pos = pDoc->GetStartPosition();
while (pos)
{
CShowStringCntrItem* pCurrentItem =
(CShowStringCntrItem*) pDoc->GetNextClientItem(pos);
if ( pCurrentItem->m_rect.PtInRect(point) )
{
pHitItem = pCurrentItem;
}
}
return pHitItem;
</PRE>
<PRE>}
</PRE>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> Don't forget to add the declaration of this public function to the
header file.
<HR>
</BLOCKQUOTE>
<P>This function is given a CPoint that describes the point on the screen where the
user clicked. Each container item has a rectangle, m_rect, as you saw earlier, and
the CRect class has a member function called PtInRect() that takes a CPoint and returns
TRUE if the point is in the rectangle or FALSE if it is not. This code simply loops
through the items in this document, using the OLE document member function GetNextClientItem(),
and calls PtInRect() for each.</P>
<P>What happens if there are several items in the container, and the user clicks
at a point where two or more overlap? The one on top is selected. That's because
GetStartPosition() returns a pointer to the bottom item, and GetNextClientItem()
works its way up through the items. If two items cover the spot where the user clicked,
pHitItem is set to the lower one first, and then on a later iteration of the while
loop, it is set to the higher one. The pointer to the higher item is returned.</P>
<P>
<H3><A NAME="Heading7"></A>Drawing Multiple Items</H3>
<P>While that code to loop through all the items is still fresh in your mind, why
not fix CShowStringView::OnDraw() so it draws all the items? Leave all the code that
draws the string, and replace the code in Listing 14.27 with that in Listing 14.28.</P>
<P>
<H4>Listing 14.27  ShowStringView.cpp--Lines in OnDraw() to Replace</H4>
<PRE> // Draw the selection at an arbitrary position. This code should
// be removed once your real drawing code is implemented. This
// position corresponds exactly to the rectangle returned by
// CShowStringCntrItem, to give the effect of in-place editing.
// TODO: remove this code when final draw code is complete.
if (m_pSelection == NULL)
{
POSITION pos = pDoc->GetStartPosition();
m_pSelection = (CShowStringCntrItem*)pDoc->GetNextClientItem(pos);
}
if (m_pSelection != NULL)
{
m_pSelection->Draw(pDC, m_pSelection->m_rect);
CRectTracker trackrect;
SetupTracker(m_pSelection,&trackrect);
trackrect.Draw(pDC);
</PRE>
<PRE> }
</PRE>
<H4>Listing 14.28  ShowStringView.cpp--New Lines in OnDraw()</H4>
<PRE> POSITION pos = pDoc->GetStartPosition();
while (pos)
{
CShowStringCntrItem* pCurrentItem =
(CShowStringCntrItem*) pDoc->GetNextClientItem(pos);
pCurrentItem->Draw(pDC, pCurrentItem->m_rect);
if (pCurrentItem == m_pSelection )
{
CRectTracker trackrect;
SetupTracker(pCurrentItem,&trackrect);
trackrect.Draw(pDC);
}
</PRE>
<PRE> }
</PRE>
<P>Now each item is drawn, starting from the bottom and working up, and if it is
selected, it gets a tracker rectangle.</P>
<P>
<H3><A NAME="Heading8"></A>Handling Single Clicks</H3>
<P>When the user clicks the client area of the application, a WM_LBUTTONDOWN message
is sent. This message should be caught by the view. Right-click CShowStringView in
ClassView, and choose Add Windows Message Handler from the shortcut menu. Click WM_LBUTTONDOWN
in the New Windows Messages/Events box on the left (see Figure 14.10), and then click
Add and Edit to add a handler function and edit the code immediately.</P>
<P><A HREF="javascript:popUp('14uvc10.gif')"><B>FIG. 14.10</B></A><B> </B><I>Add
a function to handle left mouse button clicks.</I></P>
<P>Add the code in Listing 14.29 to the empty OnLButtonDown() that Add Windows Message
Handler generated.</P>
<P>
<H4>Listing 14.29  ShowStringView.cpp--CShowStringView::OnLButtonDown()</H4>
<PRE>void CShowStringView::OnLButtonDown(UINT nFlags, CPoint point)
{
CShowStringCntrItem* pHitItem = HitTest(point);
SetSelection(pHitItem);
if (pHitItem == NULL)
return;
CRectTracker track;
SetupTracker(pHitItem, &track);
UpdateWindow();
if (track.Track(this,point))
{
Invalidate();
pHitItem->m_rect = track.m_rect;
GetDocument()->SetModifiedFlag();
}
</PRE>
<PRE>}
</PRE>
<P>This code determines which item has been selected and sets it. (SetSelection()
isn't written yet.) Then, if something has been selected, it draws a tracker rectangle
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -