Programming Examples

Are you a Programmer or Application Developer or a DBA? Take a cup of coffee, sit back and spend few minutes here :)

MFC Owner Drawn Menu Using WM_DRAWITEM, WM_MEASUREITEM

1. Introduction to Owner Drawn Menu

When an owner window of the menu item decides how the menu item should look, then the menu item is known as Owner Drawn Menu. The default windows provided menu has a standard look and feel. We cannot add Red and blue-colored boxes as a menu item through the resource editor. In this Example, we will see how can we display only color boxes as a menu item under the color menu. Here, we will learn the need for the WM_DRAWITEM and WM_MEASUREITEM windows messages.

2. Prepare SDI Application Menu Items for Owner Draw

First, we will create MFC SDI Application and then we write code for Owner Drawn Menu. To create MFC SDI Application, in the app wizard, we have to select Single Document Interface option. Then we have to remove the check mark for the Document/View architecture support check box. This example does not need it as Mainframe is the owner of the Menu Item. The below picture shows this.

Creating MFC SDI Application for Owner Drawn Menu Example
Creating MFC SDI Application for Owner Drawn Menu Example

In the class files provided by the application wizard, we will draw the owner-draw menu items through CMainFrame Class. After creating the application, we have to remove the unwanted Menus and Menu Items from the Menu Bar of the Mainframe Window. Then, we will add the color menu at the end of the menu bar. To this color menu, we will add three menu items named Red, Blue, and Green. The below video shows this (No Audio):

Video 1: Prepare MFC Menu Items (No Audio)

Note that the menu items which we saw in the above videos are not Owner-Draw menu items. We will make these menu items as an owner-draw menu item at run-time. Once the three menu items are added, we name it as ID_COLOR_RED, ID_COLOR_BLUE, and ID_COLOR_GREEN  using Property Window. For Example, the below picture shows assigning the ID to the menu item Green. One can do the same for other two Menu Items.

Setting Resource ID for the Owner Drawn Menu Item
Setting Resource ID for the Owner Drawn Menu Item

At this stage, running the application will display the standard menu items with texts Red, Blue, and Green. Now we will start our coding for this example.

3. ModifyMenu & MF_OWNERDRAW

Video 1 shows that we already added three menu items to the color menu. However, these menu items are not Owner-Drawn. To make it Owner Drawn, we should change the menu items by calling the ModifyMenu on the CMenu. We have to Add the below-specified code in the OnCreate function of the CMainFrame.

The code added (Sample 01) above will change three Menu Items of the ‘color’ as Owner Drawn Menu. The first parameter; for example, ID_COLOR_RED specifies what Menu Item we are changing. In the second parameter, we specified that the first parameter given to the function is a command id of the Menu Item. This is done by supplying the flag MF_BYCOMMAND. We can also specify a Menu Item by its position in that case the first parameter is a position number like 0,1,2 etc and the second parameter supplies the flag MF_BYPOSITION. In the second parameter, we also supplied one more flag MF_OWNERDRAW which makes the Menu Item as an Owner Drawn Menu Item. We can even change the command id of the Menu Item as part of this modification using the third parameter. In our case, we kept the same IDs.

Here, we created the menu first, then used the ModifyMenu Function to change the menu items as Owner Drawn. We can also use the AppendMenu or InsertMenu with the MF_OWNERDRAW flag. In both the cases, we are creating the menu at run-time. Note, since the menu items are tagged as MF_OWNERDRAW, windows will now send WM_DRAWITEM and WM_MEASUREITEM messages.

4. Single Handler For All Three Owner Drawn Menu Items

We will add single handler function for all three changed Menu Items. First, we ensure that Resource IDs for the Menu Items are in sequential order. In other words, the IDS of the Menu Items should be a continuous numbers. In our case, ID_COLOR_RED is lower id and ID_COLOR_GREEN is higher id. We can check these IDS from Resource.h as shown in the below picture.

Menu Item Resource IDS in Sequential Order
Menu Item Resource IDS in Sequential Order

Once we have the ID sequence, make an entry in the message map as shown below:

In ON_COMMAND_RANGE Macro, we link handler function OnOwnerDMenuClick to all our three Menu Items. The IDs passed to the Macro defines the Lower and Upper boundary. This way, when we click any Menu Item under the color Menu of our example, the handler function receives the call.

In the MainFrame.h header file, we add the handler function declaration as shown below:

And, Below is the implementation for the handler function in the MainFrame.cpp:

The above handler function displays the message boxes, which corresponds to the clicked menu item. In the above function we check the ‘ cid’ which carries the command id of the menu item clicked.

5. WM_MEASUREITEM & WM_DRAWITEM

When we specify a Menu Item as Owner Drawn Menu, the owner, the CMainFrame window will receive WM_DRAWITEM and WM_MEASUREITEM  window messages. The WM_MEASUREITEM message will be sent only once for each Owner Drawn Menu Item. This means, MFC sends the message when the user opens the Menu containing the Menu Items for the first time. The handler for the measure item will set the dimensions required for drawing those menus.

MFC Sends the WM_DRAWITEM windows message for every Owner Drawn Menu Item whenever the menu is opened by the mouse click. Therefore, the owner draws each Menu Item in the WM_DRAWITEM handler by using the measurements taken from the handler function of the WM_MEASUREITEM.

Let us say, for example, let us consider that user clicks the color Menu three times. For the first time the CFrameWnd will hear both the window messages. For second and third clicks, it will get only the WM_DRAWITEM message. The Below video shows providing the handler function in CFrameWnd for both the window messages:

Providing the Handler Functions (No Audio)

6. OnMeasureItem Handler For WM_MEASUREITEM

In the previous video, we saw the Handler Function for both the window messages discussed in the previous section. Now we will add the below piece of code in the OnMeasureItem Handler of WM_MEASUREITEM messages.

In the above OnMeasureItem handler function, we specified the width and height of our Owner Drawn Menu Item with a hard-coded value through LPMEASUREITEMSTRUCT. Therefore, in our case, the three Menu Items will have same width and height. Sometimes, the width and height changes for each Menu Item. For Example, displaying the name of the Font in the Menu using the same Font Style. This changes the width and height of the Menu Items based on the displayed Font’s characteristics even if we display same text.

7. OnDrawItem Handler For WM_DRAWITEM

Look at the handler function signature of the OnDrawItem below:

The measurement done for each menu item in the OnMeasureItem can be retrieved here in the OnDrawItem using the lpDrawItemStruct. Before we write the code inside the OnDrawItem, we explore some important members of the LPDRAWITEMSTRUCT.

  1. The ‘ itemId’ member of LPDRAWITEMSTRUCT holds menu item id. Using this, we can know what menu item we are drawing now in the OnDrawItem handler. If we want to draw special effect for a particular menu item, this member will be useful.
  2. rcItem’ member provides the rectangular structure. We can use this structure to get the dimensions for drawing the Menu Item.
  3. The ‘ hDC’ member which we can use for Drawing the shapes using the GDI objects.
  4. The ‘ itemState’ member informs the state of the menu item. We can check the state using the bit-wise & operator with constants ODS_CHECKED, ODS_DISABLED, ODS_GRAYED and ODS_SELECTED.

7.1 Create MFC Device Context For the Owner Drawn Menu

First thing we will do is creating a Device Context for the drawing. Once the device context is created in the stack, we attach this MFC object to the ‘hDC’ win32 handle of the lpDrawItemStruct structure. Remember, MFC gives this structure as a parameter to the OnDrawItem handler.

7.2 Draw Menu Border

When user hovers the menu over a Menu Item, we should highlight it. Hence, we will draw a border in black color around it. In addition, we will draw the border around Non-selected Menu items in the system default background color of the Menu. This will differentiate the selected Menu Item from the normal one.

The Win32 API call, GetSysColor(COLOR_MENU) will give us the system default background for the Menu. Drawing around the Non-selected Menu Item is required to clear the already drawn black rectangle. For example, when the user moves the mouse cursor from Red Menu Item to Blue Menu Item, we should clear the Border Rectangle around the red and draw a new border around the blue one. To draw the rectangle, we call FrameRect function on the device context. The ODS_SELECTED constant is checked against the item state of the Menu Item to decide the color of the brush. Below is the additional code we added in the OnDrawItem:

7.3 Pick Color Brush & Draw Menu Item Tiles

We check the itemID member with the menu item id constant for creating the color brushes matching the selected menu item. Once the required color brush is created, we fill the diminished rectangle measured in the OnMeasureItem. The DeflateRect function will reduce the dimension of the rectangle and FillRect paints the rectangle using the brush specified. After drawing the color tiles, we detach the Win32 handle from CDC object. Below is the code which draws color tile in the OnDrawItem:

Now we completed the coding for owner drawn color tiled menu items. The below video shows how the menu items look.

Owner Drawn Color Menu Tiles (No Audio)

Source Code : Download From Google Drive

Categories: MFC

Tags: , , , ,

Do you like this Example? Please comment about it for others!!

This site uses Akismet to reduce spam. Learn how your comment data is processed.