|
|
1.1 ! root 1: Microsoft Foundation Classes Microsoft Corporation ! 2: Technical Notes ! 3: ! 4: #14 : Custom Controls and other topics ! 5: ! 6: This note describes the custom control support in MFC, how self ! 7: drawing controls are supported as well as the interface and use ! 8: of the CBitmapButton class. ! 9: ! 10: Also dynamic subclassing is described, as well as general advice on ! 11: ownership of CWnd objects v.s. HWNDs. ! 12: ! 13: The MFC sample application 'CTRLTEST' illustrates many of these ! 14: features. Please refer to the source code to that sample ! 15: (in \C700\MFC\SAMPLES\CTRLTEST) as well as the general ! 16: README.TXT for the samples (\C700\MFC\SAMPLES\README.TXT). ! 17: ! 18: ============================================================================= ! 19: Custom Control Interface ! 20: ======================== ! 21: ! 22: Owner Draw Controls/Menus: ! 23: -------------------------- ! 24: ! 25: Windows provides support for "owner draw" for controls and menus. ! 26: These are windows messages sent to a parent window of a control or ! 27: menu that allow you customize the visual appearance and behaviour ! 28: of the control or menu. ! 29: ! 30: MFC directly supports owner draw with the message map entries: ! 31: CWnd::OnDrawItem ! 32: CWnd::OnMeasureItem ! 33: CWnd::OnCompareItem ! 34: CWnd::OnDeleteItem ! 35: ! 36: You can override these in your CWnd derived class (usually a dialog ! 37: or main frame window) to implement the owner draw behaviour, ! 38: just as it is done in the C API. ! 39: ! 40: This approach does not lead to reusable code. If you have two ! 41: similar controls in two different dialogs, you must implement ! 42: the custom control behavior in two places. ! 43: The MFC supported self drawing control architecture solves this problem. ! 44: ! 45: Self Drawing Controls/Menus: ! 46: ---------------------------- ! 47: ! 48: MFC provides a default implementation (in CWnd) for the standard ! 49: owner draw messages. This default implementation will decode ! 50: the owner draw parameters, and delegate the owner draw messages ! 51: to the controls or menu. This is called "self draw" since the ! 52: drawing (/measuring/comparing) code is in the class of the control ! 53: or menu, not in the owner window. ! 54: ! 55: This allows you to build reusable control classes that display ! 56: using "owner draw" semantics, only the code for drawing the ! 57: control is in the control class and not the owner. This is ! 58: an object-oriented approach to custom control programming. ! 59: ! 60: For self draw buttons: ! 61: CButton:DrawItem(LPDRAWITEMSTRUCT); ! 62: // draw this button ! 63: ! 64: For self draw menus: ! 65: CMenu:MeasureItem(LPMEASUREITEMSTRUCT); ! 66: // measure the size of an item in this menu ! 67: CMenu:DrawItem(LPDRAWITEMSTRUCT); ! 68: // draw an item in this menu ! 69: ! 70: For self draw listboxes: ! 71: CListBox:MeasureItem(LPMEASUREITEMSTRUCT); ! 72: // measure the size of an item in this listbox ! 73: CListBox:DrawItem(LPDRAWITEMSTRUCT); ! 74: // draw an item in this listbox ! 75: ! 76: CListBox:CompareItem(LPCOMPAREITEMSTRUCT); ! 77: // compare two items in this listbox if LBS_SORT ! 78: CListBox:DeleteItem(LPDELETEITEMSTRUCT); ! 79: // delete an item from this listbox ! 80: ! 81: For self draw comboboxes: ! 82: CComboBox:MeasureItem(LPMEASUREITEMSTRUCT); ! 83: // measure the size of an item in this combobox ! 84: CComboBox:DrawItem(LPDRAWITEMSTRUCT); ! 85: // draw an item in this combobox ! 86: ! 87: CComboBox:CompareItem(LPCOMPAREITEMSTRUCT); ! 88: // compare two items in this combobox if CBS_SORT ! 89: CComboBox:DeleteItem(LPDELETEITEMSTRUCT); ! 90: // delete an item from this combobox ! 91: ! 92: ! 93: For details on the owner draw structures, DRAWITEMSTRUCT, MEASUREITEMSTRUCT, ! 94: COMPAREITEMSTRUCT and DELETEITEMSTRUCT please refer to the MFC ! 95: documentation for CWnd::OnDrawItem, OnMeasureItem, OnCompareItem, ! 96: and OnDeleteItem respectively. ! 97: ! 98: You do not have to examine the 'CtlType' or 'CtlID' fields of these ! 99: structures (MFC does that for you). ! 100: ! 101: For self drawing menus you must override both MeasureItem and DrawItem ! 102: member functions. ! 103: ! 104: For self drawing listboxes and comboboxes you must override MeasureItem ! 105: and DrawItem. You must specify the OWNERDRAWVARIABLE style in the dialog ! 106: template (LBS_OWNERDRAWVARIABLE and CBS_OWNERDRAWVARIABLE respectively). ! 107: The OWNERDRAWFIXED style will not work with self drawing items since ! 108: the fixed item height is determined before self drawing controls ! 109: are attached to the listbox (the Win 3.1 member functions ! 110: CListBox::SetItemHeight and CComboBox::SetItemHeight can be used ! 111: to get around this limitation). ! 112: ! 113: For self drawing listboxes and comboboxes with the SORT style ! 114: (LBS_SORT and CBS_SORT respectively) you must override the ! 115: CompareItem member function. ! 116: ! 117: ! 118: Examples of Self Drawing Controls/Menus: ! 119: ---------------------------------------- ! 120: The CTRLTEST sample application (\c700\mfc\samples\ctrltest) provides ! 121: samples of an self draw menu (showing colors) and an self draw ! 122: listbox (also showing colors). ! 123: ! 124: The most typical example of a self drawing button is a bitmap ! 125: button (a button that shows one, two or three bitmap images ! 126: for the different states). This is so common we have provided ! 127: a class in MFC that directly provides this functionality. ! 128: (see CBitmapButton below). ! 129: ! 130: ============================================================================= ! 131: CBitmapButton: ! 132: ============== ! 133: ! 134: The CBitmapButton class is derived from CButton and provides a ! 135: self-draw implementation of a push button. This button uses ! 136: bitmaps instead of text for the face of the button. ! 137: ! 138: Creating a bitmap button: ! 139: Here are the steps to create a bitmap button and use it in ! 140: a dialog. This just shows one way of using this class. ! 141: There are more options available using the CBitmapButton class. ! 142: 1) Create the bitmaps: ! 143: Create one, two or three bitmaps using IMAGEDIT.EXE. ! 144: Normally you should create all three. ! 145: The first bitmap is for the "up" button state, the second ! 146: for the "down" button state. The third bitmap is for the ! 147: "focused" button state which used when the input focus is ! 148: on the button (usually the same as "up" but with a heavier ! 149: border). All bitmaps should be the same size, but there ! 150: is no restriction on that size. ! 151: ! 152: For each button, pick an image name for that button ! 153: (up to 7 characters, eg: "MYIMAGE"). ! 154: Save the bitmaps to three separate files with file ! 155: names ending in "U", "D" and "F", all with the .BMP ! 156: extension, for example: MYIMAGEU.BMP, MYIMAGED.BMP, ! 157: and MYIMAGEF.BMP. ! 158: ! 159: 2) Placing a bitmap button in a dialog: ! 160: Using DLGEDIT to edit your dialog. Add an owner-draw ! 161: button wherever you want to have a bitmap button ! 162: (add a pushbutton and set the "Owner-Draw" style). ! 163: Set the text to the image name (eg: "MYIMAGE"), ! 164: and define a symbol for that button (eg: IDC_MYIMAGE). ! 165: The size of the button you draw on the dialog does ! 166: not matter, since the default CBitmapButton autoload ! 167: routine will resize it to the exact size of the bitmap. ! 168: ! 169: 3) Add the bitmaps to your .RC file: ! 170: Each of the bitmaps should be included in the RC file ! 171: with the same name as the file, for example: ! 172: MYIMAGEU bitmap MYIMAGEU.BMP ! 173: MYIMAGED bitmap MYIMAGED.BMP ! 174: MYIMAGEF bitmap MYIMAGEF.BMP ! 175: ! 176: Note the choice of "U", "D" and "F" is not arbitrary, ! 177: the AutoLoad function relies on these particular names. ! 178: ! 179: 3) Aliasing the dialog control with the C++ CBitmapButton object: ! 180: Create a C++ dialog class (derived from CModalDialog usually). ! 181: For each bitmap button in the dialog, have a CBitmapButton ! 182: member object (the name of the member is not important). ! 183: In the OnInitDialog routine for your dialog, call AutoLoad ! 184: for each bitmap button (passing the control ID for the button ! 185: and the dialog pointer). ! 186: The AutoLoad member function will load in the bitmaps (based ! 187: on the image name we set up earlier). AutoLoad will also ! 188: attach the dialog control to the C++ CBitmapButton object ! 189: using SubclassDlgItem (SubclassDlgItem is a very general ! 190: control/window aliasing mechanism that is described in detail below). ! 191: ! 192: class CMyDlg : ... ! 193: { ! 194: CBitmapButton m_mybtn; ! 195: ... ! 196: }; ! 197: ! 198: BOOL CMyDlg::OnInitDialog() ! 199: { ! 200: VERIFY(m_mybtn.AutoLoad(IDC_MYIMAGE, this)); ! 201: // verify we have loaded the image into the button with ! 202: // control ID of IDC_MYIMAGE ! 203: ... ! 204: } ! 205: ! 206: ! 207: Examples of CBitmapButtons: ! 208: --------------------------- ! 209: See CTRLTEST and SPEAKN for examples of bitmap buttons. ! 210: ! 211: Member functions of CBitmapButton: ! 212: ---------------------------------- ! 213: ! 214: CBitmapButton two constructors are provided, one with no ! 215: parameters that does nothing, and another ! 216: with 3 parameters that will load the ! 217: bitmaps for you. ! 218: LoadBitmaps loads the bitmaps from the named resource ! 219: The first bitmap is required, the other ! 220: two are optional. ! 221: SizeToContent resizes the button to the size of the first ! 222: bitmap ! 223: ! 224: AutoLoad does everything: ! 225: * loads in the bitmaps based on the text ! 226: of the button + suffices "U", "D" and "F" ! 227: * resizes the button to content ! 228: * dynamically subclasses the button object ! 229: ! 230: ============================================================================= ! 231: Dynamic Subclassing: ! 232: ==================== ! 233: ! 234: Subclassing is the Windows term for replacing the WndProc of a ! 235: window with a different WndProc, and calling the old WndProc ! 236: for default (super class) functionality. ! 237: ! 238: This should not be confused with C++ class derivation (C++ terminology ! 239: uses the words "base" and "derived" while the Windows object model ! 240: uses "super" and "sub"). C++ derivation with MFC and Windows subclassing ! 241: are very similar in functionality - except for the fact C++ does ! 242: not support a feature similar to dynamic subclassing. ! 243: ! 244: The CWnd class provides the connection between a C++ object (derived ! 245: from CWnd) and a Windows window object (aka an HWND). ! 246: ! 247: There are three common ways these are related: ! 248: * CWnd creates the HWND. The behaviour can be modified in a derived class. ! 249: This is a case of "class derivation" and is done by creating ! 250: a class derived from CWnd and created with calls to 'Create'. ! 251: * CWnd gets attached to an existing HWND. The behaviour of the ! 252: existing window is not modified. ! 253: This is a case of "delegation" and is made possible by ! 254: calling 'Attach' to alias an existing HWND to a CWnd C++ ! 255: object. ! 256: * CWnd gets attached to an existing HWND and you can modify ! 257: the behaviour in a derived class. ! 258: This is called "dynamic subclassing" since we are changing ! 259: the behaviour (and hence the class) of a Windows object at runtime. ! 260: ! 261: This last case is done with the member functions: ! 262: CWnd::SubclassWindow and SubclassDlgItem. ! 263: ! 264: Both routines attach a CWnd object to an existing Windows HWND. ! 265: SubclassWindow takes the HWND directly, and SubclassDlgItem is ! 266: a helper that takes a control ID and the parent window (usually ! 267: a dialog). SubclassDlgItem is designed for attaching C++ objects ! 268: to dialog controls created from a dialog template. ! 269: ! 270: Please refer to the CTRLTEST example for several examples of ! 271: when to use SubclassWindow and SubclassDlgItem. ! 272: ! 273: ============================================================================= ! 274: Cleanup: CWnd::PostNCDestroy: ! 275: ============================= ! 276: ! 277: The following is an important topic. If you follow the guidelines ! 278: set out below, you will have very few problems with cleanup problems ! 279: (either in forgetting to delete/free C++ memory, forgetting to ! 280: free up system resources like HWNDs, or freeing objects too many times). ! 281: ! 282: There are typically four kinds of CWnd derived objects: ! 283: * child windows / controls (derived from CWnd) ! 284: * main frame windows (MDI and SDI included, derived from CFrameWnd) ! 285: * modeless dialogs (derived from CDialog) ! 286: * modal dialogs (derived from CModalDialog) ! 287: ! 288: ! 289: Here are the recommended ownership rules: ! 290: * child windows / controls should be embedded as members in ! 291: their parent window / dialog class. They will get automatically ! 292: cleaned up when the parent window / dialog object gets ! 293: destroyed. ! 294: * main frame windows should be allocated with 'new'. ! 295: The standard application startup will do this, eg: ! 296: m_pMainWnd = new CMyMainWindow; ! 297: * related to the above: main frame windows should not be ! 298: embedded as members in other classes/objects. ! 299: * main frame windows should not be deleted with the 'delete' ! 300: operator, but use DeleteWindow instead. ! 301: * modeless dialogs usually follow the same rules as main frame ! 302: windows - but you must override PostNcDestroy yourself ! 303: to call "delete this". ! 304: * modal dialogs should be embedded on the frame (i.e. auto ! 305: variables) and get cleaned up when the routine using ! 306: the dialog returns. ! 307: ! 308: When destroying a Windows window, the last windows message sent to ! 309: the window is 'WM_NCDESTROY'. The default CWnd handler for that ! 310: message (CWnd::OnNcDestroy) will detach the HWND from the C++ ! 311: object and call the virtual function 'PostNcDestroy'. ! 312: ! 313: The default PostNcDestroy implementation does nothing. ! 314: The CFrameWnd implementation will delete the C++ object. ! 315: ! 316: When to override PostNcDestroy?: ! 317: * if you have a CFrameWnd derived class embedded in another ! 318: class or statically allocated. ! 319: * if you have a modeless dialog you want to automatically ! 320: cleanup. ! 321: ! 322: For example, if you wish to have a frame window class that can ! 323: be allocated on the stack frame, as a static member, or embedded in ! 324: another object, you would derive your own class as ! 325: usual. You would, in addition, override the virtual member function ! 326: PostNcDestroy as follows: ! 327: ! 328: class CMyFrameWnd : public CFrameWnd ! 329: // Frame window class that can be allocated on the stack or ! 330: // in static memory. ! 331: { ! 332: // standard function overrides, constructors, message handlers ! 333: ! 334: protected: ! 335: virtual void PostNcDestroy(); ! 336: }; ! 337: ! 338: void CMyFrameWnd::PostNcDestroy() ! 339: { ! 340: // do nothing ! 341: } ! 342: ! 343: The owner of the object is responsible for calling DestroyWindow() and ! 344: making sure the object is properly cleaned up. This is demonstrated ! 345: in the CTRLTEST sample application. ! 346: ! 347: =============================================================================
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.