Annotation of mstools/mfc/doc/tn014.txt, revision 1.1.1.1

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: =============================================================================

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.