Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 139 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
139
Dung lượng
1,9 MB
Nội dung
although the window scrolls OK, if you try to draw more elements with the view scrolled, things don’t work as they should. The elements appear in a different position from where you draw them and they’re not displayed properly. What’s going on? Logical Coordinates and Client Coordinates The problem is the coordinate systems that you’re using — and that plural is deliberate. You’ve actually been using two coordinate systems in all the examples up to now, although you may not have noticed. As you saw in the previous chapter, when you call a function such as LineTo(), it assumes that the argu- ments passed are logical coordinates. The function is a member of the CDC class that defines a device con- text, and the device context has its own system of logical coordinates. The mapping mode, which is a property of the device context, determines what the unit of measurement is for the coordinates when you draw something. The coordinate data that you receive along with the mouse messages, on the other hand, has nothing to do with the device context or the CDC object — and outside of a device context, logical coordinates don’t apply. The points passed to the OnLButtonDown() and OnMouseMove() handlers have coordinates that are always in device units, that is, pixels, and are measured relative to the upper-left corner of the client area. These are referred to as client coordinates. Similarly, when you call InvalidateRect(), the rectan- gle is assumed to be defined in terms of client coordinates. In MM_TEXT mode, the client coordinates and the logical coordinates in the device context are both in units of pixels, and so they’re the same — as long as you don’t scroll the window. In all the previous examples there was no scrolling, so everything worked without any problems. With the latest version of Sketcher, it all works fine until you scroll the view, whereupon the logical coordinates origin (the 0,0 point) is moved by the scrolling mechanism, so it’s no longer in the same place as the client coordinates origin. The units for logical coordinates and client coordinates are the same here, but the origins for the two coordinates systems are different. This situation is illustrated in Figure 16-11. Figure 16-11 939 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 939 The left side shows the position in the client area where you draw, and the points that are the mouse posi- tions defining the line. These are recorded in client coordinates. The right side shows where the line is actu- ally drawn. Drawing is in logical coordinates, but you have been using client coordinate values. In the case of the scrolled window, the line appears displaced due to the logical origin being relocated. This means that you are actually using the wrong values to define elements in the Sketcher program, and when you invalidate areas of the client area to get them redrawn, the rectangles passed to the function are also wrong — hence, the weird behavior of the program. With other mapping modes it gets worse, because not only are the units of measurement in the two coordinate systems different, but also the y axes may be in opposite directions! Dealing with Client Coordinates Consider what needs to be done to fix the problem. There are two things you may have to address: ❑ You need to convert the client coordinates that you got with mouse messages to logical coordi- nates before you can use them to create elements. ❑ You need to convert a bounding rectangle that you created in logical coordinates back to client coordinates if you want to use it in a call to InvalidateRect(). This amounts to making sure you always use logical coordinates when using device context functions, and always use client coordinates for other communications about the window. The functions you have to apply to do the conversions are associated with a device context, so you need to obtain a device con- text whenever you want to convert from logical to client coordinates, or vice versa. You can use the coor- dinate conversion functions of the CDC class inherited by CClientDC to do the work. The new version of the OnLButtonDown() handler incorporating this is: // Handler for left mouse button down message void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point) { CClientDC aDC(this); // Create a device context OnPrepareDC(&aDC); // Get origin adjusted aDC.DPtoLP(&point); // convert point to Logical m_FirstPoint = point; // Record the cursor position SetCapture(); // Capture subsequent mouse messages } You obtain a device context for the current view by creating a CClientDC object and passing the pointer this to the constructor. The advantage of CClientDC is that it automatically releases the device context when the object goes out of scope. It’s important that device contexts are not retained, as there are a limited number available from Windows and you could run out of them. If you use CClientDC, you’re always safe. As you’re using CScrollView, the OnPrepareDC() member function inherited from that class must be called to set the origin for the logical coordinate system in the device context to correspond with the scrolled position. After you have set the origin by this call, you use the function DPtoLP(), which converts from Device Points to Logical Points, to convert the point value that’s passed to the handler to logical coordi- nates. You then store the converted point, ready for creating an element in the OnMouseMove() handler. The new code for the OnMouseMove() handler is as follows: void CSketcherView::OnMouseMove(UINT nFlags, CPoint point) 940 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 940 { CClientDC aDC(this); // Device context for the current view OnPrepareDC(&aDC); // Get origin adjusted if((nFlags&MK_LBUTTON)&&(this==GetCapture())) { aDC.DPtoLP(&point); // convert point to Logical m_SecondPoint = point; // Save the current cursor position // Rest of the function as before } The code for the conversion of the point value passed to the handler is essentially the same as in the previ- ous handler, and that’s all you need here for the moment. The last function that you must change is easy to overlook: the OnUpdate() function in the view class. This needs to be modified to: void CSketcherView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { // Invalidate the area corresponding to the element pointed to // if there is one, otherwise invalidate the whole client area if(pHint) { CClientDC aDC(this); // Create a device context OnPrepareDC(&aDC); // Get origin adjusted // Get the enclosing rectangle and convert to client coordinates CRect aRect=((CElement*)pHint)->GetBoundRect(); aDC.LPtoDP(aRect); InvalidateRect(aRect); // Get the area redrawn } else InvalidateRect(0); // Invalidate the client area } The modification here creates a CClientDC object and uses the LPtoDP() function member to convert the rectangle for the area that’s to be redrawn to client coordinates. If you now compile and execute Sketcher with the modifications I have discussed and are lucky enough not to have introduced any typos, it will work correctly, regardless of the scroller position. Using MM_LOENGLISH Mapping Mode Now look into what you need to do to use the MM_LOENGLISH mapping mode. This provides drawings in logical units of 0.01 inches, and also ensures that the drawing size is consistent on displays at different resolutions. This makes the application much more satisfactory from the users’ point of view. You can set the mapping mode in the call to SetScrollSizes() made from the OnInitialUpdate() function in the view class. You also need to specify the total drawing area, so, if you define it as 3000 by 3000, this provides a drawing area of 30 inches by 30 inches, which should be adequate. The default scroll distances for a line and a page is satisfactory, so you don’t need to specify those. You can use Class View to get to the OnInitialUpdate() function and then change it to the following: void CSketcherView::OnInitialUpdate(void) 941 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 941 { CScrollView::OnInitialUpdate(); // Define document size as 30x30ins in MM_LOENGLISH CSize DocSize(3000,3000); // Set mapping mode and document size. SetScrollSizes(MM_LOENGLISH, DocSize); } You just alter the arguments in the call to SetScrollSizes() for the mapping mode and document the size that you want. That’s all that’s necessary to enable the view to work in MM_LOENGLISH, but you still need to fix how you deal with rectangles. Note that you are not limited to setting the mapping mode once and for all. You can change the mapping mode in a device context at any time and draw different parts of the image to be displayed using dif- ferent mapping modes. A function SetMapMode() is used to do this, but I won’t be going into this any further here. You can get your application working just using MM_LOENGLISH. Whenever you create a CClientDC object for the view and call OnPrepareDC(), the device context that it owns has the map- ping mode you’ve set in the OnInitialUpdate() function. The problem you have with rectangles is that the element classes all assume the mapping mode is MM_TEXT, and in MM_LOENGLISH the rectangles are upside down because of the reversal of the y-axis. When you apply LPtoDP() to a rectangle, it is assumed to be oriented properly with respect to the MM_LOENGLISH axes. Because yours are not, the function mirrors the rectangles in the x-axis. This creates a problem when you call InvalidateRect() to invalidate an area of a view because the mirrored rectangle in device coordinates is not recognized by Windows as being inside the visible client area. You have two options for dealing with this. You can modify the element classes so that the enclosing rec- tangles are the right way up for MM_LOENGLISH, or you can re-normalize the rectangle that you intend to pass to the InvalidateRect() function. The latter is the easiest course because you only need to modify one member of the view class, OnUpdate(): void CSketcherView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { // Invalidate the area corresponding to the element pointed to // if there is one, otherwise invalidate the whole client area if(pHint) { CClientDC aDC(this); // Create a device context OnPrepareDC(&aDC); // Get origin adjusted // Get the enclosing rectangle and convert to client coordinates CRect aRect=((CElement*)pHint)->GetBoundRect(); aDC.LPtoDP(aRect); aRect.NormalizeRect(); InvalidateRect(aRect); // Get the area redrawn } else InvalidateRect(0); } 942 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 942 That should do it for the program as it stands. If you rebuild Sketcher, you should have scrolling work- ing, with support for multiple views. You’ll need to remember to re-normalize any rectangle that you convert to device coordinates for use with InvalidateRect() in the future. Any reverse conversions are also affected. Deleting and Moving Shapes Being able to delete shapes is a fundamental requirement in a drawing program. One question relating to this is how you’re going to select the element you want to delete. Of course, after you decide how to select an element, this applies equally well if you want to move an element, so you can treat moving and deleting elements as related problems. But first consider how you’re going to bring move and delete operations into the program. A neat way of providing move and delete functions would be to have a pop-up context menu appear at the cursor position when you click the right mouse button. You could then put Move and Delete as items on the menu. A pop-up that works like this is a very handy facility that you can use in lots of different situations. How should the pop-up be used? The standard way that context menus work is that the user moves the mouse over a particular object and right-clicks on it. This selects the object and pops up a menu contain- ing a list of items, which relate to actions that can be performed on that object. This means that different objects can have different menus. You can see this in action in Developer Studio itself. When you right- click on a class icon in Class View, you get a menu that’s different from the one you get if you right-click on the icon for a member function. The menu that appears is sensitive to the context of the cursor, hence the term “context menu.” You have two contexts to consider in Sketcher. You could right-click with the cursor over an element, and you could right-click when there is no element under the cursor. So, how can you implement this functionality in the Sketcher application? You can do it simply by creating two menus: one for when you have an element under the cursor, and one for when you don’t. You can check if there’s an element under the cursor when the user presses the right mouse button. If there is an element under the cursor, you can highlight the element so that the user knows exactly which element the context pop-up is referring to. Take a look at how you can create a pop-up at the cursor and, after that works, come back to how to implement the detail of the move and delete operations. Implementing a Context Menu The first step is to create a menu containing two pop-ups: one containing Move and Delete as items, the other a combination of items from the Element and Color menus. So, change to Resource View and expand the list of resources. Right-click on the Menu folder to bring up a context menu — another demonstration of what you are now trying to create in the Sketcher application. Select Insert Menu to create a new menu. This has a default ID IDR_MENU1 assigned, but you can change this. Select the name of the new menu in the 943 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 943 Resource View and display the Properties window for the resource by pressing Alt+Enter (This is a short- cut to the View > Other Windows > Properties Window menu item). You can then edit the resource ID in the Properties window by clicking the value for the ID. You could change it to something more suit- able, such as IDR_CURSOR_MENU, in the right column. Note that the name for a menu resource must start with IDR. Pressing the Enter key saves the new name. You can now create two new items on the menu bar in the Editor pane. These can have any old captions because they won’t actually be seen by the user. They represents the two context menus that you provide with Sketcher, so you can name them element and no element, according to the situation in which the context menu will be used. Now you can add the Move and Delete items to the element pop-up. The default IDs of ID_ELEMENT_MOVE and ID_ELEMENT_DELETE will do fine, but you could change them if you wanted to in the Properties window for each item. Figure 16-12 shows how the new element menu looks. Figure 16-12 The second menu contains the list of available element types and colors, identical to the items on the Element and Color menus on the main menu bar, but here separated by a Separator. The IDs you use for these items must be the same as you applied to the IDR_SketcherTYPE menu. This is because the han- dler for a menu is associated with the menu ID. Menu items with the same ID use the same handlers, so the same handler is used for the Line menu item regardless of whether it’s invoked from the main menu pop-up or from the context menu. You have a shortcut that saves you having to create all these menu items one-by-one. If you display the IDR_SketcherTYPE menu and extend the Element menu, you can select all the menu items by clicking the first item and then by clicking the last item while holding down the Shift key. You can then right- click the selection and select Copy from the pop-up or simply press Ctrl+C. If you then return to the IDR_CURSOR_MENU and right-click the first item on the no-element menu, you can insert the complete contents of the Element menu by selecting Paste from the pop-up or by pressing Ctrl+V. The copied menu items will have the same IDs as the originals. To insert the separator, just right-click the empty menu item and select Insert Separator from the pop-up. Repeat the process for the Color menu items and you’re done — almost. Putting the items from the Element and Color menus together has created a conflict — both Rectangle and Red share the same shortcut. Changing &Red to Re&d here will fix it, and it’s a good idea to change it on in the IDRSketcherTYPE menu too for consistency. You do this by edit- ing the Caption property for the menu item. The completed menu should look as shown in Figure 16-13. Close the properties box and save the resource file. At the moment, all you have is the definition of the menu in a resource file. It isn’t connected to the code in the Sketcher program. You now need to associate 944 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 944 this menu and its ID, IDR_CURSOR_MENU, with the view class. You also must create command handlers for the menu items in the pop-up corresponding to the IDs ID_MOVE and ID_DELETE. Figure 16-13 Associating a Menu with a Class To associate the context menu with the view class in Sketcher, go to the Class View pane and display the Properties window for CSketcherView by right-clicking the class name and selecting Properties from the pop-up. If you click the Messages button in the Properties window, you’ll be able to add a handler for the WM_CONTEXTMENU message by selecting <Add>OnContextMenu from the adjacent cell in the right column. You can then add the following code to the handler: void CSketcherView::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu menu; menu.LoadMenu(IDR_CURSOR_MENU); // Load the context menu CMenu* pPopup = menu.GetSubMenu(0); // Get the first menu ASSERT(pPopup != NULL); // Ensure it’s there // Display the popup menu pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); } For now, this handler arbitrarily displays the first of the two context menus. You still need to figure out how you’ll determine whether or not the cursor is over an element to decide which menu to display, but we’ll come back to that a little later. Calling the LoadMenu() method for the menu object loads the menu resource corresponding to the ID supplied as the argument and attaches it to the CMenu object menu. The GetSubMenu() function returns a pointer to the pop-up menu corresponding to the integer argument that specifies the position of the pop-up, with 0 being the first pop-up, 1 being the second, and so on. After you ensure the pointer returned by GetSubMenu() is not NULL, you display the pop-up by calling TrackPopupMenu(). 945 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:18 AM Page 945 The first argument to the TrackPopupMenu() function consists of two flags ORed together. One flag specifies how the pop-up menu should be positioned and can be any of the following values: The second flag specifies the mouse button and can be either of the following: The next two arguments to the TrackPopupMenu() function specify the x and y coordinates of the pop- up menu on the screen respectively. The y coordinate determines to position of the top of the menu. The fourth argument specifies the window that owns the menu and that should receive all WM_COMMAND mes- sages from the menu. Now you can add the handlers for the items in the first pop-up menu. Return to the Resource View and double-click on IDR_CURSOR_MENU. Right-click the Move menu item and then select Add Event Handler from the pop-up. You can then specify the handler in the dialog for the Event Handler wizard, as shown in Figure 16-14. It’s a COMMAND handler and is to be created in the CSketcherView class. Click the Add and Edit button to create the handler function. You can follow the same procedure to create the hander for the Delete menu item. You don’t have to do anything for the second context menu, as you already have handlers written for them in the document class. These take care of the messages from the pop-up items automatically. Choosing a Context Menu At the moment, the OnContextMenu() handler only displays the first context pop-up, no matter where the right button is clicked in the view. This isn’t really what you want it to do. The first context menu applies specifically to an element, whereas the second context menu applies in general. You want to display the first menu if there is an element under the cursor and to display the second menu if there isn’t. Flag Description TPM_LEFTMOUSEBUTTON Specifies that the pop-up tracks the left mouse button. TPM_RIGHTMOUSEBUTTON Specifies that the pop-up tracks the right mouse button. Flag Description TPM_CENTERALIGN Centers the pop-up horizontally relative to the x coordinate supplied as the second argument to the function. TPM_LEFTALIGN Positions the pop-up so that the left side of the menu is aligned with the x coordinate supplied as the second argument to the function. TPM_RIGHTALIGN Positions the pop-up so that the right side of the menu is aligned with the x coordinate supplied as the second argument to the function. 946 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:19 AM Page 946 Figure 16-14 You need two things to fix this up: You need a mechanism to find out which (if any) element is at the cur- rent cursor position, and you need to save the address of this element somewhere so you can use it in the OnContextMenu() handler. You can deal with saving the address of the element first because this is the easier bit. When you find out which element is under the cursor, you’ll store its address in a data member, m_pSelected, of the view class. This is available to the right mouse button handler because that’s in the same class. You can add the declaration for this variable to the protected section of the CSketcherView class: class CSketcherView: public CScrollView { // Rest of the class as before protected: CPoint m_FirstPoint; // First point recorded for an element CPoint m_SecondPoint; // Second point recorded for an element CElement* m_pTempElement; // Pointer to temporary element CElement* m_pSelected; // Currently selected element // Rest of the class as before }; You can add this manually or alternatively, you can right-click the class name and select Add > AddVariable from the pop-up to open the dialog for adding a data member. If you add 947 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:19 AM Page 947 m_pSelected manually you’ll also need to initialize this element in the class constructor, so add the fol- lowing code: CSketcherView::CSketcherView() : m_FirstPoint(CPoint(0,0)) , m_SecondPoint(CPoint(0,0)) , m_pTempElement(NULL) , m_pSelected(NULL) { // TODO: add construction code here } You’ll figure out how to decide when an element is under the cursor in a moment, but in the meantime you can use the m_pSelected member of the view in the implementation of the OnContextMenu() handler: void CSketcherView::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu menu; menu.LoadMenu(IDR_CURSOR_MENU); CMenu* pPopup = menu.GetSubMenu(m_pSelected == 0 ? 1 : 0); ASSERT(pPopup != NULL); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); } The expression m_pSelected == 0 ? 1 : 0 results in 1 when the pointer is null and 0 otherwise; so you’ll select the first pop-up menu containing Move and Delete when m_pSelected is not null and the second pop-up when it is. Identifying a Selected Element To keep track of which element is under the cursor you can add code to the OnMouseMove() handler in the CSketcherView class. This handler is called every time the mouse cursor moves, so all you have to do is add code to test whether there’s an element under the current cursor position and set m_pSelected accordingly. The test whether a particular element is under the cursor is simple; if the cursor position is within the bounding rectangle for an element, that element is under the cursor. Here’s how you can mod- ify the OnMouseMove() handler to check if there’s an element under the cursor: void CSketcherView::OnMouseMove(UINT nFlags, CPoint point) { // Define a Device Context object for the view CClientDC aDC(this); // DC is for this view OnPrepareDC(&aDC); // Get origin adjusted CSketcherDoc* pDoc=GetDocument(); // Get a pointer to the document CElement* pElement = 0; // Store an element pointer CRect aRect(0,0,0,0); // Store a rectangle POSITION aPos = pDoc->GetListHeadPosition(); // Get first element position m_pSelected = 0; while(aPos) // Iterate through the list { pElement = pDoc->GetNext(aPos); aRect = pElement->GetBoundRect(); aDC.LPtoDP(aRect); // Convert to device coordinates 948 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/08 9:19 AM Page 948 [...]... the element pointer is valid, // find the pointer in the list and delete it POSITION aPosition = m_ElementList.Find(pElement); m_ElementList.RemoveAt(aPosition); 957 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 9 58 Chapter 16: Creating the Document and Improving the View delete pElement; // Delete the element from the heap } } You shouldn’t have any trouble understanding how this works After making sure... CSketcherView::CSketcherView() : m_FirstPoint(CPoint(0,0)) , m_SecondPoint(CPoint(0,0)) , m_pTempElement(NULL) , m_pSelected(NULL) , m_MoveMode(FALSE) , m_CursorPos(CPoint(0,0)) , m_FirstPos(CPoint(0,0)) 9 58 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 959 Chapter 16: Creating the Document and Improving the View { // TODO: add construction code here } The element move process starts when the Move menu item from the context... p2.Y; int maxX = p1.X > p2.X ? p1.X : p2.X; int maxY = p1.Y > p2.Y ? p1.Y : p2.Y; int width = Math::Max(2, maxX - minX); int height = Math::Max(2, maxY - minY); 967 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 9 68 Chapter 16: Creating the Document and Improving the View boundRect = System::Drawing::Rectangle(minX, minY, width, height); } void Add(Point p) { points->push_back(Point(p.X-position.X, p.Y-position.Y));... System::Drawing::Rectangle object corresponds to the sum of the x-coordinate for the top left position and the width and the Bottom property are the sum of the y-coordinate and the height; 9 68 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 969 Chapter 16: Creating the Document and Improving the View these properties are set only There are also Left and Top properties that are set only properties, and these correspond... function in each of the classes derived from CElement in exactly the same way For example, you should change the CLine class definition to: class CLine : public CElement 952 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 953 Chapter 16: Creating the Document and Improving the View { public: ~CLine(void); // Function to display a line virtual void Draw(CDC* pDC, CElement* pElement=0); // Constructor for a... to add the definition for SELECT_COLOR to the OurConstants.h file: //Definitions of constants #pragma once // Element type definitions // Each type value must be unique 953 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 954 Chapter 16: Creating the Document and Improving the View const unsigned int LINE = 101U; const unsigned int RECTANGLE = 102U; const unsigned int CIRCLE = 103U; const unsigned int CURVE... values for drawing const COLORREF BLACK = RGB(0,0,0); const COLORREF RED = RGB(255,0,0); const COLORREF GREEN = RGB(0,255,0); const COLORREF BLUE = RGB(0,0,255); const COLORREF SELECT_COLOR = RGB(255,0, 180 ); /////////////////////////////////// Now you should add an #include directive for OurConstants.h to the CElements.cpp file to make the definition of SELECT_COLOR available You have nearly implemented... 0 } // Create a temporary element of the type and color that // is recorded in the document object, and draw it m_pTempElement = CreateElement(); // Create a new element 954 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 955 Chapter 16: Creating the Document and Improving the View m_pTempElement->Draw(&aDC); // Draw the element } else { // We are not drawing an element so do highlighting CSketcherDoc* pDoc=GetDocument();... one To do this you save the value of m_pSelected in pOldSelection You then search for an element under the cursor and if there is one, you store its address in m_pSelected 955 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 956 Chapter 16: Creating the Document and Improving the View If pOldSelection and m_pSelected are equal then either they both contain the address of the same element or they are both zero... provide code in the bodies of the handlers for the Move and Delete menu items that you added earlier You can add the code for Delete first, as that’s the simpler of the two 956 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 957 Chapter 16: Creating the Document and Improving the View Deleting an Element The code that you need in the OnElementDelete() handler in the CSketcherView class to delete the currently . pElement->GetBoundRect(); aDC.LPtoDP(aRect); // Convert to device coordinates 9 48 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 9 48 aRect.NormalizeRect(); // Renormalize the. m_CursorPos(CPoint(0,0)) , m_FirstPos(CPoint(0,0)) 9 58 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/ 08 9:19 AM Page 9 58 . CSketcherView::OnInitialUpdate(void) 941 Chapter 16: Creating the Document and Improving the View 25905c16.qxd:WroxPro 2/21/ 08 9: 18 AM Page 941 { CScrollView::OnInitialUpdate(); // Define document size as 30x30ins in MM_LOENGLISH CSize