Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 122 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
122
Dung lượng
2,16 MB
Nội dung
void CSketcherView::OnRButtonDown(UINT nFlags, CPoint point) { if(m_MoveMode) { // In moving mode, so drop element back in original position CClientDC aDC(this); OnPrepareDC(&aDC); // Get origin adjusted MoveElement(aDC, m_FirstPos); // Move element to orig position m_MoveMode = FALSE; // Kill move mode m_pSelected = 0; // De-select element GetDocument()->UpdateAllViews(0); // Redraw all the views return; // We are done } } You first create a CClientDC object for use in the MoveElement() function. You then call the MoveElement() function to move the currently selected element the distance from the current cursor position to the original cursor position that we saved in m_FirstPos. After the element has been reposi- tioned, you just turn off move mode, deselect the element, and get all the views redrawn. Exercising the Application Everything is now complete for the context pop-ups to work. If you build Sketcher, you can select the element type and color from one context menu, or if you are over an element, you can then move or delete that element from the other context menu. Dealing with Masked Elements There’s still a limitation that you might want to get over. If the element you want to move or delete is enclosed by the rectangle of another element that is drawn after the element you want, you won’t be able to highlight it because Sketcher always finds the outer element first. The outer element completely masks the element it encloses. This is a result of the sequence of elements in the list. You could fix this by adding a Send to Back item to the context menu that would move an element to the beginning of the list. Add a separator and a menu item to the element drop-down in the IDR_CURSOR_MENU resource as shown in Figure 15-15. Figure 15-15 814 Chapter 15 18_571974 ch15.qxp 1/20/06 11:48 PM Page 814 You can add a handler for the item to the view class through the Properties window for the CSketcherView class. It’s best to handle it in the view because that’s where you record the selected element. Select the Messages toolbar button in the Properties window for the class and double-click the message ID ID_ELEMENT_SENDTOBACK. You’ll then be able to select COMMAND below and <Add> OnElementSendtoback in the right column. You can implement the handler as: void CSketcherView:: OnElementSendtoback() { GetDocument()->SendToBack(m_pSelected); // Move element in list } You’ll get the document to do the work by passing the currently selected element pointer to a public function SendToBack() that you implement in the CSketcherDoc class. Add it to the class definition with a void return type, and a parameter of type CElement*. You can implement this function as: void CSketcherDoc::SendToBack(CElement* pElement) { if(pElement) { // If the element pointer is valid, // find the pointer in the list and remove the element POSITION aPosition = m_ElementList.Find(pElement); m_ElementList.RemoveAt(aPosition); m_ElementList.AddTail(pElement); // Put it back to the end of the list } } After you have the POSITION value corresponding to the element, you remove the element from the list by calling RemoveAt(). Of course, this does not delete the element from memory; it just removes the pointer to it from the list. You then add the element pointer back at the end of the list using the AddTail() function. With the element moved to the end of the list, it cannot mask any of the others because you search from the beginning. You will always find one of the other elements first if the applicable bounding rectangle encloses the current cursor position. The Send to Back menu option is always able to resolve any element masking problem in the view. Summary In this chapter, you’ve seen how to apply MFC collection classes to the problems of managing objects and managing pointers to objects. Collections are a real asset in programming for Windows because the application data that you store in a document often originates in an unstructured and unpredictable way, and you need to be able traverse the data whenever a view needs to be updated. You have also seen how to create document data and manage it in a pointer list in the document, and in the context of the Sketcher application, how the views and the document communicate with each other. 815 Creating the Document and Improving the View 18_571974 ch15.qxp 1/20/06 11:48 PM Page 815 You have improved the view capability in Sketcher in several ways. You’ve added scrolling to the views using the MFC class CScrollView, and you’ve introduced a pop-up at the cursor for moving and delet- ing elements. You have also implemented an element highlighting feature to provide the user with feed- back when moving or deleting elements. You have covered quite a lot of ground in this chapter, and some of the important points you need to keep in mind are: ❑ If you need a collection class to manage your objects or pointers, the best choice is one of the template-based collection classes because they provide type-safe operation in most cases. ❑ When you draw in a device context, coordinates are in logical units that depend on the mapping mode set. Points in a window that are supplied along with Windows mouse messages are in client coordinates. The two coordinate systems are usually not the same. ❑ Coordinates that define the position of the cursor are in screen coordinates that are measured in pixels relative to the upper- left corner of the screen. ❑ Functions to convert between client coordinates and logical coordinates are available in the CDC class. ❑ Windows requests that a view is redrawn by sending a WM_PAINT message to your application. This causes the OnDraw() member of the affected view to be called. ❑ You should always do any permanent drawing of a document in the OnDraw() member of the view class. This ensures that the window is drawn properly when required by Windows. ❑ You can make your OnDraw() implementation more efficient by calling the RectVisible() member of the CDC class to check whether an entity needs to be drawn. ❑ To get multiple views updated when you change the document contents, you can call the UpdateAllViews() member of the document object. This causes the OnUpdate() member of each view to be called. ❑ You can pass information to the UpdateAllViews() function to indicate which area in the view needs to be redrawn. This makes redrawing the views faster. ❑ You can display a context menu at the cursor position in response to a right mouse click. This menu is created as a normal pop-up. Exercises You can download the source code for the examples in the book and the solutions to the following exer- cises from http://www.wrox.com. 1. Implement the CCurve class so that points are added to the head of the list instead of the tail. 2. Implement the CCurve class in the Sketcher program using a typed pointer list, instead of a list of objects to represent a curve. 3. Look up the CArray template collection class in Help, and use it to store points in the CCurve class in the Sketcher program. 816 Chapter 15 18_571974 ch15.qxp 1/20/06 11:48 PM Page 816 16 Working with Dialogs and Controls Dialogs and controls are basic tools for user communications in the Windows environment. In this chapter, you’ll learn how to implement dialogs and controls by applying them to extend the Sketcher program. As you do so, you’ll learn about: ❑ Dialogs and how you can create dialog resources ❑ Controls are and how to add them to a dialog ❑ Basic varieties of controls available to you ❑ How to create a dialog class to manage a dialog ❑ How to program the creation of a dialog box and how to get information back from the controls in it ❑ Modal and modeless dialogs ❑ How to implement and use direct data exchange and validation with controls ❑ How to implement view scaling ❑ How you can add a status bar to an application Understanding Dialogs Of course, dialog boxes are not new to you. Most Windows programs of consequence use dialogs to manage some of their data input. You click a menu item and up pops a dialog box with various controls that you use for entering information. Just about everything that appears in a dialog box is a control. A dialog box is actually a window and, in fact, each of the controls in a dialog is also a specialized window. Come to think of it, most things you see on the screen under Windows are windows. 19_571974 ch16.qxp 1/20/06 11:35 PM Page 817 Although controls have a particular association with dialog boxes, you can also create and use them in other windows if you want to. A typical dialog box is illustrated in Figure 16-1. Figure 16-1 This is the File > Open > File dialog in Visual C++ 2005. The annotations show the variety of con- trols used that combine to provide an intuitive interface for selecting a file to be opened. This makes the dialog easy to use, even though there’s a whole range of possibilities here. There are two things needed to create and display a dialog box in an MFC program: the physical appear- ance of the dialog box, which is defined in a resource file, and a dialog class object used to manage the operation of the dialog and its controls. MFC provides a class called CDialog for you to use after you have defined your dialog resource. Understanding Controls There are many different controls available to you in Windows, and in most cases there’s flexibility in how they look and operate. Most of them fall into one of the following six categories: Toolbar buttons ButtonsButtons Combo Box List Box List Boxes 818 Chapter 16 19_571974 ch16.qxp 1/20/06 11:35 PM Page 818 Control Type What They Do Static Controls These are used to provide titles or descriptive information. Button Controls Buttons provide a single-click input mechanism. There are basically three fla- vors of button controls, simple push buttons, radio buttons where only one may be in a selected state at any one time, and checkboxes where several may be in a selected state at one time. Scrollbars Scrollbars are typically used to scroll text or images either horizontally or ver- tically within another control. List Boxes These present a list of choices and one or more selections can be in effect at one time. Edit Controls Edit controls allow text input or editing of text that is displayed. Combo boxes Combo boxes present a list of choices from which you can select combined with the option of entering text yourself. Figure 16-2 shows some examples of various types of controls. Figure 16-2 A list box presents a predefined list of items from which you can choose. The scroll bar need not be present for a short list. A list can also have multiple columns and be scrolled horizontally. A version of the list box is available that can display icons as well as text. Comboboxes combine the capability of a dropdown list from which you can select with the option of entering data yourself. The Save As dialog uses a combobox to enable you to enter the file name. You have already seen scroll bars attached to the client area of the Sketcher window. Scroll bars can also be free standing. This text box is the simplest form of edit control that allows you to enter and/or edit a line of text. More sophisticated edit controls can display multiple lines of text and support scrolling of the text. Static controls provide static information, such as titles or instructions, or simply provide decoration in a dialog in the form of an icon or a filled rectangle. Radio buttons are usually grouped so that if one is checked all the others are unchecked. Check boxes are individually checked and more than one can be checked at one time. Buttons can have labels as here and they can also display icons. 819 Working with Dialogs and Controls 19_571974 ch16.qxp 1/20/06 11:35 PM Page 819 A control may or may not be associated with a class object. Static controls don’t do anything directly, so an associated class object may seem superfluous; however, there’s an MFC class, CStatic, that provides functions to enable you to alter the appearance of static controls. Button controls can also be handled by the dialog object in many cases, but again MFC does provide the CButton class for use in situations where you need a class object to manage a control. MFC also provides a full complement of classes to support the other controls. Because a control is a window, they are all derived from CWnd. Common Controls The set of standard controls that are supported by MFC and the Resource editor are called common con- trols. Common controls include all of the controls you have just seen, as well as other more complex controls such as the animate control, for example, which has the capability to play an AVI (Audio Video Interleaved) file, and the tree control that can display a hierarchy of items in a tree. Another useful control in the set of common controls is the spin button. You can use this to increment or decrement values in an associated edit control. To go into all of the possible controls that you might use is beyond the scope of this book, so I’ll just take a few illustrative examples (including an example that uses a spin button) and implement them in the Sketcher program. Creating a Dialog Resource Here’s a concrete example. You could add a dialog to Sketcher to provide a choice of pen widths for drawing elements. This ultimately involves modifying the current pen width in the document, as well as in the CElement class, and adding or modifying functions to deal with pen widths. You’ll deal with all that, though, after you’ve got the dialog together. Display the Resource View, expand the resource tree for Sketcher, and right-click the Dialog folder in the tree; then click Insert Dialog from the pop-up to add a new dialog resource to Sketcher. This results in the Dialog Resource editor swinging into action and displaying the dialog in the Editor pane along with the Toolbox showing a list of controls that you can add. The dialog has OK and Cancel button controls already in place. Adding more controls to the dialog is simplicity itself; you can just drag the control from the palette to the position where you want to place it in the dialog. Alternatively, you can click a control from the list to select it and then click in the dialog where you want the control to be positioned. When it appears you’ll still be able to move it around to set its exact position, and you’ll also be able to resize it by dragging handles on the boundaries. The dialog has a default ID assigned that is IDD_DIALOG1, but it would be better to have an ID that was a bit more meaningful. You can edit the ID by right-clicking the dialog name in the Resource View pane and selecting Properties from the pop-up. You can also display the dialog’s properties by right-clicking in the Dialog Editor pane and selecting from the pop-up. Change the ID to something that relates to the purpose of the dialog such as IDD_PENWIDTH_DLG. At the same time, you could also change the Caption property value to Set Pen Width. Adding Controls to a Dialog Box To provide a mechanism for entering a pen width, you can add controls to the basic dialog that’s initially displayed until it looks like the one shown in Figure 16-3. 820 Chapter 16 19_571974 ch16.qxp 1/20/06 11:35 PM Page 820 Figure 16-3 Figure 16-3 shows the grid that you can use to position controls. If the grid is not displayed, you can select the appropriate Toolbar button to display it; the Toolbar button toggles the grid on and off. Alternatively, you can display rules along the side and top of the dialog that you can use to create guide lines as shown in Figure 16-4. Figure 16-4 You create a horizontal guide by clicking the appropriate rule. You can position a guide line by dragging the arrow for it along the rule and then using one or more guides when positioning a control. The dialog has six radio buttons that provide the pen width options. These are enclosed within a group box with the caption Pen Widths. The group box serves to enclose the radio buttons and make them operate as a group, where only one member of the group can be checked at any given time. Each radio button has an appropriate label to identify the pen width that is set when selected. There are also the default OK and Cancel buttons that close the dialog. Each of the controls in the dialog has its own set of properties that you can access and modify in the same way as for the dialog box itself. The next step is to add the group box. As I said, the group box serves to associate the radio buttons in a group from an operational standpoint, and to provide a caption and a boundary for the group of but- tons. Where you need more than one set of radio buttons, a means of grouping them is essential if they 821 Working with Dialogs and Controls 19_571974 ch16.qxp 1/20/06 11:35 PM Page 821 are to work properly. You can select the button corresponding to the group box from the common con- trols palette by clicking it; then click the approximate position in the dialog box where you want the center of the group box. This places a group box of default size on to the dialog. You can then drag the borders of the group box to enlarge it to accommodate the six radio buttons that you add. To set the cap- tion for the group box, type the caption you want (in this, case type Pen Widths). The last step is to add the radio buttons. Select the radio button control by clicking it and then clicking on the position in the dialog where you want to position a radio button within the group box. Do the same for all six radio buttons. For each button you can select it by clicking it; then type in the caption to change it. You can also drag the border of the button to set its size, if necessary. To display the properties window for a control, select it by clicking it; then select Properties from the pop-up. You can change the ID for each radio button in the properties window for the control to correspond better with its pur- pose: IDC_PENWIDTH0 for the 1 pixel pen width, IDC_PENWIDTH1 for the 0.01 inch width pen, IDC_ PENWIDTH2 for the 0.02 inch pen, and so on. You can position individual controls by dragging them around with the mouse. You can also select a group of controls by selecting successive controls with the Shift key pressed, or by dragging the cursor with the left button pressed to create a rectangle enclosing them. To align a group of controls, select the appropriate button from the Dialog Editor toolbar shown in Figure 16-5. Figure 16-5 The toolbar is shown in its undocked state — that is, dragged away from the toolbar area at the top of the window. If the toolbar is not visible, you can show it by right-clicking in the toolbar area and selecting it in the list of toolbars that is displayed. You also can align controls in the dialog by selecting from the Format menu. Testing the Dialog The dialog resource is now complete. You can test it by selecting the Toolbar button that appears at the left end of the toolbar in Figure 16-5 or by pressing Ctrl+T. This displays the dialog window with the basic operations of the controls available, so you can try clicking on the radio buttons. When you have a group of radio buttons, only one can be selected. As you select one, any other that was previously selected is reset. Click either of the OK or Cancel buttons or even the Close icon in the title bar for dialog to end the test. After you have saved the dialog resource, you’re ready to add some code to support it. Programming for a Dialog There are two aspects to programming for a dialog: getting it displayed, and handling the effects of its controls. Before you can display the dialog corresponding to the resource you’ve just created, you must first define a dialog class for it. The Class wizard helps with this. 822 Chapter 16 19_571974 ch16.qxp 1/20/06 11:35 PM Page 822 Adding a Dialog Class Right-click the dialog box that you just created in the Resource Editor pane and then select Add Class from the pop-up tool display the Class Wizard dialog. You’ll define a new dialog class derived from the MFC class CDialog, so select that class name from the Base Class: drop-down list box. You can enter the class name as CPenDialog in the Class name: edit box. The Class Wizard dialog should look as shown in Figure 16-6. Figure 16-6 Click the Finish button to create the new class. The CDialog class is a window class (derived from the MFC class CWnd) that’s specifically for displaying and managing dialogs. The dialog resource that you have created automatically associates with an object of type CPenDialog because the IDD class member is initialized with the ID of the dialog resource: class CPenDialog : public CDialog { DECLARE_DYNAMIC(CPenDialog) public: CPenDialog(CWnd* pParent = NULL); // standard constructor virtual ~CPenDialog(); // Dialog Data enum { IDD = IDD_PENWIDTH_DLG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support DECLARE_MESSAGE_MAP() }; 823 Working with Dialogs and Controls 19_571974 ch16.qxp 1/20/06 11:35 PM Page 823 [...]... bar prompt text, separated from it by “\n” Here the value for the Prompt property is “Change pen width\nShow pen width options” The menu will look as shown in Figure 16 -8 824 Working with Dialogs and Controls Figure 16-7 Figure 16 -8 825 Chapter 16 To add the Toolbar button, open the toolbar resource by extending the Toolbar folder in the Resource View and double-clicking IDR_MAINFRAME You can add a... and minimum values for the variable m_Scale For our application, a minimum of 1 and a maximum of 8 would be good values Note that this constraint only applies to the edit box; the spin control is independent of it Figure 16-17 shows how the window for the Add Member wizard should look when you are done 83 8 Working with Dialogs and Controls Figure 16-17 If you click the Finish button, the wizard takes... dialog box is displayed You can add the function to the CPenDialog class by selecting OnInitDialog in the list of overrides in the Properties window for the CPenDialog class, as shown in Figure 16-10 82 8 Working with Dialogs and Controls Figure 16-10 The implementation for the new version of OnInitDialog() is: BOOL CPenDialog::OnInitDialog() { CDialog::OnInitDialog(); // Check the radio button corresponding... meaningful than the default, such as IDC_SPIN_SCALE Now take at look at the properties for the spin button They are shown in Figure 16-15 The menu should now look as shown in Figure 16-14 83 5 Chapter 16 Figure 16-13 83 6 Figure 16-14 Working with Dialogs and Controls Figure 16-15 The Arrow Keys property is already set as True, enabling you to operate the spin button by using arrow keys on the keyboard... object Because the handler declares a CPenDialog object, you must add a #include statement for PenDialog.h to the beginning of SketcherDoc.cpp (after the #include directives for stdafx.h and Sketcher.h); otherwise, you’ll get compilation errors when you build the program After you’ve done 82 6 Working with Dialogs and Controls that, you can build Sketcher and try out the dialog It should appear when you... CDialog::OnInitDialog(); // First get a pointer to the spin control CSpinButtonCtrl* pSpin; 84 0 Working with Dialogs and Controls pSpin = (CSpinButtonCtrl*)GetDlgItem(IDC_SPIN_SCALE); // If you have not checked the auto buddy option in // the spin control’s properties, set the buddy control here // Set the spin control range pSpin->SetRange(1, 8) ; return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION:... information Objects of type CStatusBar can also provide the same functionality as the Windows common status bar control through a member function GetStatusBarCtrl() There is an MFC class that specifically 84 8 Working with Dialogs and Controls encapsulates each of the Windows common controls — the one for the common status bar control is CStatusBarCtrl However, using this directly involves quite a bit of... m_EnclosingRect; // Initialize with the enclosing rectangle //Increase bounding rectangle by the pen width int Offset = m_Pen == 0? 1:m_Pen; // Width must be at least 1 BoundingRect.InflateRect(Offset, Offset); 83 2 Working with Dialogs and Controls return BoundingRect; } You use the local variable Offset to ensure that you pass the InflateRect() function a value of 1 if the pen width is zero (a pen width of 0... select the element using the type stored in the document switch(pDoc->GetElementType()) { case RECTANGLE: return new CRectangle(m_FirstPoint, m_SecondPoint, pDoc->GetElementColor(), pDoc->GetPenWidth()); 83 3 Chapter 16 case CIRCLE: return new CCircle(m_FirstPoint, m_SecondPoint, pDoc->GetElementColor(), pDoc->GetPenWidth()); case CURVE: return new CCurve(m_FirstPoint, m_SecondPoint, pDoc->GetElementColor(),... associated Toolbar button displays the dialog box so that you can select the pen width The screen shown in Figure 16-12 is typical of what you might see when the Sketcher program is executing Figure 16-12 83 4 Working with Dialogs and Controls Note that the dialog box is a completely separate window You can drag it around to position it where you want You can even drag it outside the Sketcher application . in Figure 16 -8. 82 4 Chapter 16 19_571974 ch16.qxp 1/20/06 11:35 PM Page 82 4 Figure 16-7 Figure 16 -8 825 Working with Dialogs and Controls 19_571974 ch16.qxp 1/20/06 11:35 PM Page 82 5 To add the. use it to store points in the CCurve class in the Sketcher program. 81 6 Chapter 15 18_ 571974 ch15.qxp 1/20/06 11: 48 PM Page 81 6 16 Working with Dialogs and Controls Dialogs and controls are basic. categories: Toolbar buttons ButtonsButtons Combo Box List Box List Boxes 81 8 Chapter 16 19_571974 ch16.qxp 1/20/06 11:35 PM Page 81 8 Control Type What They Do Static Controls These are used to provide