Advanced MFC Programming
Advanced MFC Programming Advanced MFC Programming
Chapter 2. Menu void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam) { if(nID == ID_RESUME)GetSystemMenu(TRUE); if(nID == ID_QUESTION)AfxMessageBox("Question"); CFrameWnd::OnSysCommand(nID, lParam); } 2.6 Owner-Draw Menu When we highlight a bitmap menu item by selecting it using mouse, the bitmap will be inversed. We have no way of modifying this property because function CMenu::ModifyMenu(…) requires only one bitmap, which will be used to implement menu item’s normal state. The other states of a bitmap menu item will be drawn using the default implementations. Normally this is good enough. However, sometimes we may want to use different bitmaps to represent a menu item’s different states: selected, unselected, checked, unchecked, grayed or enabled. Also, sometimes we may want to paint a menu item dynamically. Suppose we need to create a “Color” menu item: the menu item represents a color that the user can use, and this color can be modified to represent any available color in the system. To implement this type of menu item, we can paint it using the currently selected color. In this case it is not appropriate to create the menu item using bitmap resources: there may exist thousands of available colors in the system, and it is just too inconvenient to prepare a bitmap resource for each possible color. The owner-draw menu can help us build more powerful user interface. With this type of menu, we can draw the menu items dynamically, using different bitmaps to represent different states of the menu item. We can also change the associated bitmaps at any time. Overriding Two Functions The implementation of owner draw menu is not very difficult, all we need is to override two member functions of class CMenu: CMenu::MeasureItem(…) and CMenu::DrawItem(…). By default, the menu is drawn by the system. We can change this attribute by specifying MF_OWNERDRAW style for a menu. Any menu with this style will be drawn by its owner. Since a menu’s owner is usually the mainframe window (in SDI and MDI applications), we can add code to class CMainFrame to implement dynamic menu drawing. Actually, the menu drawing has two associated messages: WM_MEASUREITEM and WM_DRAWITEM. When a menu item with MF_OWNERDRAW style needs to be drawn, the menu sends out the above two messages to the mainframe window. The mainframe window finds out the pointer to the corresponding menu and calls functions CMenu::MeasureItem(…) and CMenu::DrawItem(…). Here, the first function is used to retrieve the dimension of the menu item, which can be used to calculate its layout. The second function implements menu drawing. We need to override it in order to implement custom interface. One simple and most commonly used way to provide graphic interface is to prepare a bitmap resource then load and draw it at run time. Please note that this is different from preparing a bitmap resource and calling CMenu::ModifyMenu(…) to associate the bitmap with a menu item. If we implement drawing by ourselves, we can manipulate drawing details. For example, when drawing the bitmap, we can change the size of the image, add a text over it. If we assign the bitmap to the menu item, we lose the control over the details of painting the bitmap. Drawing a Bitmap To draw a bitmap, we need to understand some basics on graphic device interface (GDI). This topic will be thoroughly discussed from chapter 8 through 12, here is just a simple discussion on bitmap drawing. In Windows operating system, when we want to output objects (a pixel, a line or a bitmap) to the screen, we can not write directly to the screen. Instead, we must write to the device context (DC). A device context is a data structure containing information about the drawing attributes of a device (typically a display or a printer). We can use DC to write text, draw pixels, lines and bitmaps to the devices. In MFC the device context is supported by class CDC, which has many functions that can let us draw different types of objects. 50
Chapter 2. Menu We can implement bitmap drawing by obtaining a target DC and calling member functions of CDC. The target DC is usually obtained from a window. A DC can select a lot of GDI objects, such pen, brush, font and bitmap. Pen can be different type of pens, and brush can be different types of brushes. A DC can select any pen or brush as its current tool, but at any time, a DC can select only one pen and one brush. If we want to draw a pixel or a line, we can select the appropriate pen into the target DC and use it to implement drawing. A DC can also select bitmap for drawing. However, to paint a bitmap, we can not select it into the target DC and draw it directly. The normal way of painting a bitmap is to prepare a compatible memory DC (which is a block of memory with the same attributes of the target DC), select the bitmap into the memory DC, and copy the bitmap from the memory DC to the target DC. We will learn more about DC and bitmap drawing in later chapters. For the time being, we can neglect the drawing details. Deriving a New Class from CMenu Sample 2.6\Menu demonstrates owner-draw menu implementation. It is base on sample 2.5\Menu. In this sample menu items ID__POPUPITEM1 and ID__POPUPITEM2 of the dynamic menu are implemented as owner-draw menu, their menu items are painted using different bitmaps. Like sample 2.5\Menu, some new images are prepared as bitmap resources. In the sample, the newly added bitmaps are IDB_BITMAP_QUESTIONSEL, IDB_BITMAP_SMILE and IDB_BITMAP_SMILESEL. We will use IDB_BITMAP_SMILE and IDB_BITMAP_SMILESEL to implement owner-draw menu item ID__POPITEM1, and use IDB_BITMAP_QUESTION to implement ID__POPITEM2. First, we need to override class CMenu. In the sample, a new class MCMenu is derived from CMenu, in this class, we declare four CBitmap type variables that will be used to load bitmaps for menu drawing. Also, functions CMenu::MeasureItem(…) and CMenu::DrawItem(…) are overridden: class MCMenu : public CMenu { protected: CBitmap m_bmpQuestion; CBitmap m_bmpQuestionSel; CBitmap m_bmpSmile; CBitmap m_bmpSmileSel; public: MCMenu(); virtual ~MCMenu(); virtual void MeasureItem(LPMEASUREITEMSTRUCT); virtual void DrawItem(LPDRAWITEMSTRUCT); }; In the constructor of class MCMenu, four bitmaps are loaded: MCMenu::MCMenu() : CMenu() { m_bmpQuestion.LoadBitmap(IDB_BITMAP_QUESTION); m_bmpQuestionSel.LoadBitmap(IDB_BITMAP_QUESTIONSEL); m_bmpSmile.LoadBitmap(IDB_BITMAP_SMILE); m_bmpSmileSel.LoadBitmap(IDB_BITMAP_SMILESEL); } Overriding Function CMenu::MeasureItem(…) Next we need to override function CMenu::MeasureItem(…). It has only one parameter, which is a pointer to MEASUREITEMSTRUCT type object. Structure MEASUREITEMSTRUCT is used to inform system the dimension of the owner-draw menu and other controls. There are three important members that will be used: itemWidth and itemHeight, which represent width and height of the menu item; itemData, from which we know the type of the menu item that is being inquired. The program can assign special data to an owner-draw menu item when it is being created. When the mainframe window calls the overridden functions to inquire the item’s attributes or paint the menu, the data 51
- Page 15 and 16: Functions Implementing Comparisons
- Page 17 and 18: Chapter 1. Tool Bar and Dialog Bar
- Page 19 and 20: Chapter 1. Tool Bar and Dialog Bar
- Page 21 and 22: Chapter 1. Tool Bar and Dialog Bar
- Page 23 and 24: Chapter 1. Tool Bar and Dialog Bar
- Page 25 and 26: Chapter 1. Tool Bar and Dialog Bar
- Page 27 and 28: Chapter 1. Tool Bar and Dialog Bar
- Page 29 and 30: Chapter 1. Tool Bar and Dialog Bar
- Page 31 and 32: Chapter 1. Tool Bar and Dialog Bar
- Page 33 and 34: Chapter 1. Tool Bar and Dialog Bar
- Page 35 and 36: Chapter 1. Tool Bar and Dialog Bar
- Page 37 and 38: Chapter 1. Tool Bar and Dialog Bar
- Page 39 and 40: Chapter 1. Tool Bar and Dialog Bar
- Page 41 and 42: Chapter 1. Tool Bar and Dialog Bar
- Page 43 and 44: Chapter 1. Tool Bar and Dialog Bar
- Page 45 and 46: Chapter 1. Tool Bar and Dialog Bar
- Page 47 and 48: Chapter 1. Tool Bar and Dialog Bar
- Page 49 and 50: Chapter 2. Menu Chapter 2 Menu Menu
- Page 51 and 52: Chapter 2. Menu ON_COMMAND(ID_EDIT_
- Page 53 and 54: Chapter 2. Menu “clipboard”. We
- Page 55 and 56: Chapter 2. Menu Using Class CMenu W
- Page 57 and 58: Chapter 2. Menu } After implementin
- Page 59 and 60: Chapter 2. Menu ); UINT nPosition,
- Page 61 and 62: Chapter 2. Menu void CMenuDoc::OnEd
- Page 63 and 64: Chapter 2. Menu } In the sample, bi
- Page 65: Chapter 2. Menu Message Mapping for
- Page 69 and 70: Chapter 2. Menu CDC *ptrDC; CDC dcM
- Page 71 and 72: Chapter 2. Menu Because the popup m
- Page 73 and 74: Chapter 3. Splitter Window Chapter
- Page 75 and 76: Chapter 3. Splitter Window function
- Page 77 and 78: Chapter 3. Splitter Window BOOL CSp
- Page 79 and 80: Chapter 3. Splitter Window In the a
- Page 81 and 82: Chapter 3. Splitter Window Sample S
- Page 83 and 84: Chapter 3. Splitter Window WM_LBUTT
- Page 85 and 86: Chapter 4. Button Chapter 4 Buttons
- Page 87 and 88: Chapter 4. Button Use string text a
- Page 89 and 90: Chapter 4. Button function, first t
- Page 91 and 92: Chapter 4. Button Now we can remove
- Page 93 and 94: Chapter 4. Button } ( m_bBmpCheck ?
- Page 95 and 96: Chapter 4. Button using bit-wise AN
- Page 97 and 98: Chapter 4. Button Overriding Functi
- Page 99 and 100: Chapter 4. Button …… First pBit
- Page 101 and 102: Chapter 4. Button Using Class MCBit
- Page 103 and 104: Chapter 4. Button The button’s ID
- Page 105 and 106: Chapter 4. Button However, since me
- Page 107 and 108: Chapter 4. Button Here, we use subc
- Page 109 and 110: Chapter 5. Common Controls command
- Page 111 and 112: Chapter 5. Common Controls ((CSpinB
- Page 113 and 114: Chapter 5. Common Controls } The in
- Page 115 and 116: Chapter 5. Common Controls …… B
Chapter 2. Menu<br />
We can implement bitmap drawing by obtaining a target DC and calling member functions of CDC. The<br />
target DC is usually obtained from a window.<br />
A DC can select a lot of GDI objects, such pen, brush, font and bitmap. Pen can be different type of<br />
pens, and brush can be different types of brushes. A DC can select any pen or brush as its current tool, but<br />
at any time, a DC can select only one pen and one brush. If we want to draw a pixel or a line, we can select<br />
the appropriate pen into the target DC and use it to implement drawing. A DC can also select bitmap for<br />
drawing. However, to paint a bitmap, we can not select it into the target DC and draw it directly. The<br />
normal way of painting a bitmap is to prepare a compatible memory DC (which is a block of memory with<br />
the same attributes of the target DC), select the bitmap into the memory DC, and copy the bitmap from the<br />
memory DC to the target DC.<br />
We will learn more about DC and bitmap drawing in later chapters. For the time being, we can neglect<br />
the drawing details.<br />
Deriving a New Class from CMenu<br />
Sample 2.6\Menu demonstrates owner-draw menu implementation. It is base on sample 2.5\Menu. In<br />
this sample menu items ID__POPUPITEM1 and ID__POPUPITEM2 of the dynamic menu are implemented as<br />
owner-draw menu, their menu items are painted using different bitmaps.<br />
Like sample 2.5\Menu, some new images are prepared as bitmap resources. In the sample, the newly<br />
added bitmaps are IDB_BITMAP_QUESTIONSEL, IDB_BITMAP_SMILE and IDB_BITMAP_SMILESEL. We will use<br />
IDB_BITMAP_SMILE and IDB_BITMAP_SMILESEL to implement owner-draw menu item ID__POPITEM1, and<br />
use IDB_BITMAP_QUESTION to implement ID__POPITEM2.<br />
First, we need to override class CMenu. In the sample, a new class MCMenu is derived from CMenu, in this<br />
class, we declare four CBitmap type variables that will be used to load bitmaps for menu drawing. Also,<br />
functions CMenu::MeasureItem(…) and CMenu::DrawItem(…) are overridden:<br />
class MCMenu : public CMenu<br />
{<br />
protected:<br />
CBitmap m_bmpQuestion;<br />
CBitmap m_bmpQuestionSel;<br />
CBitmap m_bmpSmile;<br />
CBitmap m_bmpSmileSel;<br />
public:<br />
MCMenu();<br />
virtual ~MCMenu();<br />
virtual void MeasureItem(LPMEASUREITEMSTRUCT);<br />
virtual void DrawItem(LPDRAWITEMSTRUCT);<br />
};<br />
In the constructor of class MCMenu, four bitmaps are loaded:<br />
MCMenu::MCMenu() : CMenu()<br />
{<br />
m_bmpQuestion.LoadBitmap(IDB_BITMAP_QUESTION);<br />
m_bmpQuestionSel.LoadBitmap(IDB_BITMAP_QUESTIONSEL);<br />
m_bmpSmile.LoadBitmap(IDB_BITMAP_SMILE);<br />
m_bmpSmileSel.LoadBitmap(IDB_BITMAP_SMILESEL);<br />
}<br />
Overriding Function CMenu::MeasureItem(…)<br />
Next we need to override function CMenu::MeasureItem(…). It has only one parameter, which is a<br />
pointer to MEASUREITEMSTRUCT type object. Structure MEASUREITEMSTRUCT is used to inform system the<br />
dimension of the owner-draw menu and other controls. There are three important members that will be<br />
used: itemWidth and itemHeight, which represent width and height of the menu item; itemData, from<br />
which we know the type of the menu item that is being inquired.<br />
The program can assign special data to an owner-draw menu item when it is being created. When the<br />
mainframe window calls the overridden functions to inquire the item’s attributes or paint the menu, the data<br />
51