Navigation Bar

05 December 2012

[ MFC ] - Creating Owner Draw Menu in VC++ using WM_DRAWITEM, WM_MEASUREITEM


1. Introduction


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 item. The default windows provided menu has a standard look and feel. We cannot add Red and blue boxes as menu item through the resource editor. In this article, we will see how can we display only color boxes as a menu item under the color menu.



2.  Create the SDI Application


The first thing is creating the MFC SDI Application. In the app wizard, select Single Document Interface option and make sure to uncheck the Document/View architecture support check box. This is shown in the below picture.



In the class files provided by the application wizard, we are going to draw the owner-draw menu items through CMainFrame class. Once the application is created, remove the unwanted menus from the menu bar of the mainframe window. Then add the color menu at the end of the menu bar. To this color menu, add three menu items named Red, Blue, and Green. This is shown in the below video:

Video 1: Adding the new menu items


Note that the menu items added in the above video are not an owner draw menu. We will make these menu items as an owner-draw menu item at runtime. Once the three menu items are added, name it as ID_COLOR_RED, ID_COLOR_BLUE, and ID_COLOR_GREEN. Assigning the ID to the menu item Green is shown in the below picture.


At this stage, running the application will display the standard menu items with texts Red, Blue, and Green. Let us move to coding part. To follow with me, search inside the downloaded sample with the search tag //Sample <no>

3. Change the Menu items as Owner Drawn

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 draw we should modify the menu items by calling the ModifyMenu on the CMenu. Add the below-specified code (Specified in blue color. Tag is Sample 01) in the int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

         if (!m_wndStatusBar.Create(this) ||
                 !m_wndStatusBar.SetIndicators(indicators,
                   sizeof(indicators)/sizeof(UINT)))
         {
                 TRACE0("Failed to create status bar\n");
                 return -1;      // fail to create
         }
         //Sample 01: Modify the Menu as OwnerDrawn
         CMenu * pMainFrame_menu = this->GetMenu();
         pMainFrame_menu->ModifyMenu(ID_COLOR_RED, MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_RED);
         pMainFrame_menu->ModifyMenu(ID_COLOR_BLUE, MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_BLUE);
         pMainFrame_menu->ModifyMenu(ID_COLOR_GREEN, MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_GREEN);
         // TODO: Delete these three lines if you don't want the toolbar to be dockable
         m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

The code added (Sample 01) above will modify three menu items of the “color” as owner drawn menu. The first parameter says for example ID_COLOR_RED specifies the menu item that we are modifying. In the second parameter, we specified that the first parameter given is a command id of the menu item by supplying the flag MF_BYCOMMAND. You can also specify a menu item by its position in that case a 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 that makes the menu item as an owner-drawn menu item. In the third parameter, we can change the command id of the menu item after the modification. In our case, we specified the change in the second parameter and kept the same id.
Here, we created the menu first, then used the ModifyMenu function call to change the menu items as owner drawn. You can also use the AppendMenu or InsertMenu with the MF_OWNERDRAW flag. In both the cases, we are creating the Menu at runtime.

4. One Handler – Three menu items

We are going to add single handler function for all three modified menu items. First thing is checking the order of the menu item ids that is lower to higher order with continues id numbers. In my case, ID_COLOR_RED is lower id and ID_COLOR_GREEN is higher id. You can check the ID ranges from Resource.h as shown in the below picture.


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

         //Sample 02: Hanlder for the Owner drawn menu
         ON_COMMAND_RANGE(ID_COLOR_RED, ID_COLOR_GREEN, OnOwnerDMenuClick )

Here, we specified that the handler for all the commands in between the range from ID_COLOR_RED to ID_COLOR_GREEN, the handler function is OnOwnerDMenuClick.

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

//Sample 03: Declare the Handler function
void OnOwnerDMenuClick(UINT cid);
Below is the implementation for the handler function:
//Sample 04: Handler for the Menu Items
void CMainFrame::OnOwnerDMenuClick(UINT cid)
{
         if (cid == ID_COLOR_RED)
         {
                 AfxMessageBox(_T("Red Button Clicked"));
         }
         if (cid == ID_COLOR_BLUE)
         {
                 AfxMessageBox(_T("Blue Button Clicked"));
         }
         if (cid == ID_COLOR_GREEN)
         {
                 AfxMessageBox(_T("Green Button Clicked"));
         }
}

The above handler function displays the message boxes, which corresponds to the clicked menu item. In the above function we checked the cid and that carries the command id of the clicked menu item.

5. WM_MEASUREITEM and WM_DRAWITEM

When a menu item is specified as owner drawn menu, the owner that is the CMainFrame window will receive the WM_DRAWITEM and WM_MEASUREITEM window messages. The WM_MEASUREITEM message will be sent only once for each owner draw menu item that means for the first time the menu containing the menu items is opened. The handler for the measure item will set the dimensions required for drawing those menus.
The window message WM_DRAWITEM will be sent for every owner draw menu item whenever the menu containing the owner draw menu item is opened. Therefore, the owner of the menu items actually draws the menu item here by making the measurements taken in the handler for the WM_MEASUREITEM.
For example, let us take that user clicks the color menu item three times. For the first time the CFrameWnd will receive both the window messages. For second and third times, it will receive only the WM_DRAWITEM windows message.

The Below video shows providing the handler function in CFrameWnd for both the window messages:

Video 2: Providing the Handler functions

6. Implementing OnMeasureItem

In the previous video, we saw the handler function for both the window messages discussed in the previous section of this article. Add the below piece of code in the OnMeasureItem Handler.

void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
         //Sample 05: Provide Height and widths
         UINT height = 20;
         UINT width = 40;
         lpMeasureItemStruct->itemHeight = height;
         lpMeasureItemStruct->itemWidth = width;
         CFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}

In the above handler function, we specified the width and height of our owner drawn menu item with a hard-coded value in the LPMEASUREITEMSTRUCT.  Therefore, in our case, the three menu items will have same width and height. In some cases the width and height changes for each menu item. For Ex: displaying the name of the font in the same Font. This changes the font width and height based on the displayed font even though the text displayed is same.

7. Implementing OnDrawItem

Look at the handler function signature of the OnDrawItem function shown below:

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

·       The itemId member of this structure holds menu item id. Using this we can know what menu item we are drawing now. If we want to draw some special effect for a particular menu item, this member will be useful.
·       The rcItem member provides the rectangular structure, which can be used as the dimension required for drawing the menu item.
·       The hDC member is used for Drawing the shapes using the GDI objects.
·       The itemState member informs the state of the menu item. We can check the state using the bitwise & operator with constants ODS_CHECKED, ODS_DISABLED, ODS_GRAYED and ODS_SELECTED.

1) First thing we did was creating a device context for the drawing. Once the device context dc is created in the stack, we attach this MDC object (dc) to the win32 handle hDC taken from the lpDrawItemStruct.

//6.1: Get the device context from the Draw Item structure and attach that to a dc object
CDC menu_dc;
menu_dc.Attach(lpDrawItemStruct->hDC);

2) When the menu item is selected, we are going to draw a border in black color around it. In addition, we will draw the border around non-selected menu items in the background color of the menu. The Win32 API call, GetSysColor(COLOR_MENU); will give use the background menu color.  Drawing around the Non-Selected menu item is required to clear the already drawn black rectangle. To draw the rectangle FrameRect function is called on the device context. The ODS_SELECTED constant is checked against the itemstate of the menu item to decide the color of the brush.

//6.2: Test item state. When the item is selected, draw a black border over it.
//          When item is not selected draw the border in menu's background color
//          (Clears previous drawn border)
CBrush * brush;
RECT menu_item_rct = lpDrawItemStruct->rcItem ;
if ( lpDrawItemStruct->itemState & ODS_SELECTED )
         brush = new CBrush(RGB(0,0,0));
else
{
         DWORD color_index = ::GetSysColor(COLOR_MENU);
         brush = new CBrush(color_index);
}
menu_dc.FrameRect(&menu_item_rct, brush);
delete brush;

3) 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 slightly diminished rectangle, measured in the OnMeasureItem with the color brush we created by checking the menu item id. The DeflateRect function will reduce the dimension of the rectangle and FillRect paints the rectangle using the brush specified. Once we are done with the drawing, the Win32 handle is detached from CDC object. Below is the code:

//6.3: Create the Item color and draw it
if (lpDrawItemStruct->itemID == ID_COLOR_RED)
         brush = new CBrush(RGB(255,0,0));
if (lpDrawItemStruct->itemID == ID_COLOR_BLUE)
         brush = new CBrush(RGB(0,0,255));
if (lpDrawItemStruct->itemID == ID_COLOR_GREEN)
         brush = new CBrush(RGB(0,255,0));
CRect menu_rct(menu_item_rct);
menu_rct.DeflateRect(1,2);
menu_dc.FillRect( menu_rct, brush );
delete brush;
//6.4: Detach win32 handle
menu_dc.Detach();
CFrameWnd::OnDrawItem(nIDCtl, lpDrawItemStruct);


Video 3 : Sample in action

Source Code: Download

04 November 2012

[ MFC ] - Display Context Menu On right button click using CMenu TrackPopupMenu


1.Introduction


In this article, we are going to create Main menu with four menu items in it. The last menu item is going to open a sub menu. The menu will be displayed when the mouse is right-clicked in the client area of the window and at the location of the mouse pointer.


2. About the sample


The below screenshot shows the application sample:



The sample is an SDI Application without document and view architecture support. The client area is marked in yellow border and when the mouse pointer is inside the client area of the window, the right client will display a pop-up menu.

Here we are creating the menu items at runtime and displaying the popup menu as shown in the screenshot.

The video given below shows default setting overridden for MFC SDI Application.



Video: Creating Project

3. Process WM_CONTEXTMENU

When the mouse is right clicked inside the client area of the window, the window will get a notification message WM_CONTEXTMENU. This message will come with the window in which the mouse is right clicked and pointer position in screen coordinate in which the mouse pointer lies when the click happened. This notification message will be used to display the popup menu.
The video given below shows providing the handler for the WM_CONTEXTMENU notification message. The notification is handled by the class CChildView.

Video: Adding Handler


In the video, you saw view class provides the handler for the notification message that we are discussing. The handler provided looks like below:
void CChildView::OnContextMenu(CWnd* pWnd, CPoint point)
Here, pWnd the pointer to the window in which the right client is done. The second param, point is cursor location in screen co-ordinate.

4. Display Context Menu by Handling OnContextMenu

The menu is created in side the handler provided for the WM_CONTEXTMENU.

1) First a CRect class declared to get the client window dimensions. Next, SubMenu and MainMenu instance of type CMenu is created.
//Sample 01: Declarations
CRect client_rect;
CMenu SubMenu, MainMenu;

2) We first get the client area of the window in client_rect structure. Then we convert this structure into Screen co-ordinate, which from the Top left of your monitor. We do this because the point parameter given to the handler as second argument is in screen co-ordinate.

//Sample 02: Get Mouse Click position and convert it to the Screen Co-ordinate
GetClientRect(&client_rect);
ClientToScreen(&client_rect);

3) We will display the popup context menu only when the mouse is right clicked inside the client area of the window. Therefore, we should check the mouse click position lies inside the client rectangle dimension. Note that as we get the mouse position in screen co-ordinate, so we converted the rectangle dimension client_rect into screen co-ordinate before doing the PtInrect check.

//Sample 03: Check the mouse pointer position is inside the client area
if( client_rect.PtInRect(point))
{

4) First, the Sub Menu for the context menu is created by calling the CreatePopupMenu function of the CMenu object. Then the menu items are added to it using the append menu function call.  The first parameter passed as MF_STRING says that we adding the string menu item. The second parameter is ID value that we give to the menu item. This Id will be used later when we need to process Command Message (Not covered in this article). The last parameter is display string of the menu item.

Once the Sub menu is created, we start creating the Main Menu. This menu is created the same way the sub menu is created. However, the last item on the Main menu is a sub menu that we already created. Note that we added the sub menu to this main menu; we passed the MF_POPUP as the first parameter to the function call AppendMenu. This will indicate the append menu that unlike the normal menu item the function should create the cascading menu for the menu item shown as Line Thickness.

//Sample 04: Create the sub Menu First
SubMenu.CreatePopupMenu();
SubMenu.AppendMenu(MF_STRING, 4001, _T("1"));
SubMenu.AppendMenu(MF_STRING, 4002, _T("2"));
SubMenu.AppendMenu(MF_STRING, 4003, _T("4"));
SubMenu.AppendMenu(MF_STRING, 4004, _T("8"));
//Sample 05:Create the Main Menu
MainMenu.CreatePopupMenu();
MainMenu.AppendMenu(MF_STRING, 4005, _T("Line"));
MainMenu.AppendMenu(MF_STRING, 4006, _T("Circle"));
MainMenu.AppendMenu(MF_STRING, 4007, _T("Polygon"));
MainMenu.AppendMenu(MF_POPUP, (UINT)SubMenu.GetSafeHmenu(), _T("Line Thickness"));

5) Finally, we call TrackPopupMenu to display the context that we created at runtime. The first parameter TPM_LEFTALIGN tells that the displayed popup menu should left align with the cursor location. The x and y position tells where we want to display the MainMenu as popup menu.

//Sample 06: Display the Popup Menu
MainMenu.TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);
The video given below shows the sample in action.

Video: Running the Sample


Source Code: Download

10 October 2012

[ Win32 MFC ] - File Processing using CreateFile, ReadFile and WriteFile APIs


1) Introduction


In this article, we will look at the example of using the CreateFile and OpenFile win32 API functions with a MFC Dialog based application. Win32 is a very rich API that supplies numerous functions and MFC is just a Framework wrapped over those functions to form logical functioning unit. Win32 API library is in native format that means it is in C style (Procedural Approach) whereas MFC is a OOPS based Framework API. OK, let us start with the sample.



2) About the Sample


Have a look at the below screenshot



This sample is created to copy the file content from source location to destination location. This plain file content copy operation is already supported by the operating system and this example is taken just for demo purpose. However, you can extend the operation to transform source content to destination by skipping some words or adding something to the word etc.

Specify the file name to copy in the Source File Path and specify the destination file name in which you need a copy of the source file content. The CopyFile Win32 API will do this task easily. However, in this article we are going to explore the Win32 file processing functions. The sample is created using the VC++ dialog based application.

Creating the dialog based application is shown in the below video.

Once the dialog-based application is created, control variables added to the edit box controls. This is shown in the below video:

3) Copy Content Button Handler

1) First, the win32 handles to the files are declared and these handles are hcopysource, hCopyDest. Next the variables bytes_read, bytes_written are used to store number bytes read and written depending on the file processing operation. The buffer variable is used as the cache by the program to temporarily store the data read from the file.

//Sample 01: Declarations Required
HANDLE hcopysource, hCopyDest;
DWORD bytes_read, bytes_written;
CHAR buffer [4095];

2) Next, we read input entered by the user from the text box control variables to the string variables Source_file, Dest_file. The GetWindowText functions return the text key-in in the text boxes.

//Sample 02: Get the User input
CString Source_file, Dest_file;
m_edit_ctrl_from.GetWindowText(Source_file);
m_edit_ctrl_to.GetWindowText(Dest_file);

3) The Win32 API function CreateFile is used to open the file entered by the user. The OPEN_EXISTING tag will tell the API to open the file when it already exits and fail otherwise. Once the file content that we are going to copy is opened, we store its handle in the hcopysource. The GENERIC_READ flag tells that we going to open the file for reading purpose.

//Sample 03: Create the Source File
hcopysource = ::CreateFile (Source_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hcopysource == INVALID_HANDLE_VALUE)
         AfxMessageBox("Unable to Open Source File");

4) The same way we store the destination file handle. Here it is expected that the file does not exist in the destination folder and we always try to create the file as new file in the location specified. The flag GENERIC_WRITE tells that we are going to use this file for writing something on it. The CREATE_ALWAYS attribute tells that we are always going to create the file. If it doesn’t exists in the destination location the API will create a new file and if it is there in that location then the function will just open it. Therefore, the tag always creates the file and gives back the handle.

//Sample 04: Create Destination File
hCopyDest = ::CreateFile (Dest_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hcopysource == INVALID_HANDLE_VALUE)
         AfxMessageBox("Unable to Destination Source File");

5) We use ReadFile API to read the data from the source file. Once the call succeeded, we will get the read content in the buffer variable. Note the usage of the while loop and when the file content is more that 4095 bytes, the read operation will be performed in batches. That is, 4095 or less (That will be last read) bytes will be read in each batch. The bytes_read variable will tell us how many bytes are read from the source file. Say for example, the file has 5000 bytes of data, and the first read batch will read all 4095 bytes the remaining 5 bytes are read in the next iteration. This way, we use the bytes_read variable when writing the data to the destination file using the API function WriteFile.

//Sample 05: Read the Source file content in a batch of 4095 or Lesser, then
//                        write to destination file
while (::ReadFile(hcopysource, buffer, 4095, &bytes_read, NULL) && bytes_read > 0)
{
         ::WriteFile(hCopyDest, buffer, bytes_read, &bytes_written, NULL);
         if (bytes_read != bytes_written)
         {
                 AfxMessageBox("Something Gone wrong while writing to destination. Aborting the operation");
                 ::CloseHandle(hcopysource);
                 ::CloseHandle(hCopyDest);
                 return;
         }
}
AfxMessageBox("File contents copied to destination");

6) Once the Operation is completed, we close the file HANDLES opened the button click event and display a message stating the content of the file copied to the destination.
//Sample 06: Do the Clean-Up work
m_edit_ctrl_from.SetWindowText("");
m_edit_ctrl_to.SetWindowText("");
::CloseHandle(hcopysource);
::CloseHandle(hCopyDest);
Below given video shows the Copy file operation using the Win32 API used in Dialog based MFC Application.

Source Code : Download

26 September 2012

[ MFC ] - Check Shift, ctrl, alt and Toggle keystate in onkeydown (WM_KEYDOWN Message Handler)


1. Introduction


We know that keyboard and mouse are very basic hardware interface supported by almost all the computer operating system. When the user presses the Keyboard key the device driver for the keyboard processes that input by making use of the interrupts. Windows OS knows about these kinds of hardware actions in the form of a message structure. The hardware fills this structure with the information it has processed and places that in the Operating system message queue.

The operating system message queue then places the message in the corresponding application and from the application queue; the message is delivered to the target window for responding back.

In this article, we will see how can we check various keyboard events with a simple example.



2. Create an SDI Application


The sample is a simple SDI Application.  To create the application on your own,
1. Create a new project and chose MFC Application under the Visual C++ category. Then provide the project name as shown in Fig.1
Fig.1


2. Then click ok to the above dialog. This will open a wizard now. Accept the default for the pages except the one shown in Fig.2 below:

Fig.2


The SDI Application is ready to check the different kinds of keyboard input.



3. Before we code


When a key is pressed by the user, the target window will receive the below-specified windows messages. The messages and its details are shown in Fig.3:

Fig.3


From the above table, we can conclude that KeyDown and KeyUp will be fired for any type of keyboard keys. Therefore, this event is the combination of System key as well as the character keystrokes. Let us say that you typed the alphabet letter ‘K’ in a notepad. The hardware interprets that as a two events. One is KeyDown and the other one is KeyUp. So we get three messages WM_KEYDOWN, WM_KEYUP and WM_CHAR as the typed keystroke is not a  system key.

There some special keys that we should be aware. These are categorized as Shift Keys as well as Toggle keys. Examples for shift keys are ctrl, shift and Alt. Similarly, Toggle key changes it’s key state in between on & off. One best example for toggle key is Caps Lock.

We will use the sample created in section 2 to demonstrate the WM_KEYDOWN event. You can handle other events similar to this.



4. Implement the WM_KEYDOWN event


First, we need to provide the handler for the WM_KEYDOWN message. The steps are shown in Fig.4. Go to class view and select properties for the class CKeyBoardInputView. We are going to handle our keyboard events in the view class. Next, provide the handler function for WM_KEYDOWN windows message. To do this click on the message icon, then double click on the grid column for the WM_KEYDOWN.

Fig.4


The signature of the empty handler is shown below:
void CKeyBoardInputView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
}

In this article, we are going to deal with the first parameter only. The first parameter represents the key code that you can compare with the keyboard constants to decide the keystroke.



5. Looking for Alphanumeric Key


The alphanumeric keystroke can be compared directly with character constants. In the below code we are looking for the English letter M or the numeric digit 3.

//01. Numbers and Charactors
if (nChar == 'M')
        AfxMessageBox(_T("M Pressed"));
if (nChar == '3')
        AfxMessageBox(_T("Three Pressed"));



6. Looking for the System Key


System keys should be compared with the pre-defined constant values defined in the WinUser.h header file.  The below code shows how we ensure that the typed keystroke is either an F5 key or an End key. These two keys are system keys and do not fall in the alphabets or numeric keys.

//02. Process System Keys
if (nChar == VK_F5)
        AfxMessageBox(_T("F5 Function Key Pressed"));
if (nChar == VK_END)
        AfxMessageBox(_T("END Key Pressed"));



7. Looking for the Shift key combination


Sometimes we will end up in a situation where we need to check the alphanumeric keystroke in combination with the shift keys like ctrl, shift etc. The below example will check for the ctrl+A and the Shift+S combination of the keys. When converting the 8000 from hexadecimal to binary we get 16 digits with the high bit set to one. The GetKeyState returns a negative number when the shift key is kept down. So you can either check the return value is negative or check the high order bit with the Bitwise AND operator ‘&’. The code listed uses the bit-wise operator to make the comparison:

//03. Numbers and Char keys with Shift key combination
if (nChar == 'A')
{
        if (GetKeyState(VK_CONTROL ) & 0x8000  )
               AfxMessageBox(_T("Ctrl+A is pressed"));
}

if (nChar == 'S')
{
        //Shift Keys [Alt, Shift, Ctrl]
        if (GetKeyState(VK_SHIFT) & 0x8000  )
               AfxMessageBox(_T("Shift+S is pressed"));
}



8. Looking for the Toggle key state




The toggle key like Insert and CapsLock will change their state between ON and OFF. When the toggle key is pressed it current state is inverted. In the previous section, we saw the importance of the High order bit of the nChar (First parameter of the event handler) to decide the state (Hold down) of the shift key. The low order bit is used to check the toggle key status. When the low order bit is set, the toggle key is turned on. The below code informs you whenever the CAPSLOCK key is turned-ON. Similarly, the code below will also show a message box when the NumLock is turned-Off.

//04. Checking toggle keys
if (nChar == VK_CAPITAL)
{
        if (GetKeyState(VK_CAPITAL) & 0x01 )
               AfxMessageBox(_T("Caps Key On"));
}

if (nChar == VK_NUMLOCK)
{
        if (!(GetKeyState(VK_NUMLOCK) & 0x01) )
               AfxMessageBox(_T("Num Key Off"));     
}


Source Code: Download