Annotation of mstools/mfc/doc/tn014.txt, revision 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.