Advanced MFC Programming

Advanced MFC Programming Advanced MFC Programming

math.hcmuns.edu.vn
from math.hcmuns.edu.vn More from this publisher
11.04.2014 Views

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

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!