home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK11 / MFC / DOC / TN014.TX$ / tn014
Encoding:
Text File  |  1992-03-18  |  14.5 KB  |  348 lines

  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. =============================================================================
  348.