CodeGuru content and product recommendations are
editorially independent. We may make money when you click on links
to our partners.
Learn More
Environment: Visual C++ 6
For the owner draw control, I found the most difficult part is the menu. I have searched through the Web, but I only found some difficult examples. In this article, I will demonstrates the step for drawing our own menu. We’ll make a menu class and a class for holding the menu data.
Let’s take the SDI MFC application as an example. In the File Menu, click New to add a new project. Then, choose MFC Application Wizard (exe). In the project name, type in OwnerdrawMenu (just an example), and then click OK. In step one of the MFC App Wizard, choose Single Document. After pressing Finish, you are brought to the “New Project Information” page. You’ll ignore this page, so press OK.
Let’s make our menu data class first. It is just a simple class which has one CString member variable to hold the menu item’s caption.
Right-click “OwnerdrawMenu classes” in the ClassView; then choose “New Class.” For the class type, choose “Generic Class.” In the “Name” editbox, type in “CMyMenuData” (just an example). Finally, click OK.
Add a public CString member variable which is called “m_strCaption”(just an example).
Next, we have to make a menu class. Right-click “OwnerdrawMenu classes” in the ClassView; then choose “New Class.” For the class type, choose “Generic Class.” In the “Name” editbox, type in “CMyMenu” (just an example). In the “Base Classes” section, type in “CMenu” in the “Derived From” editbox and choose “public” in the “As” listbox.
Our menu class is added. It’s time to give some content to it. We will add three functions, some include statements, and two member variables in the CMyMenu header file (such as MyMenu.h).
#include “MyMenuData.h”
#include <vector>
using namespace std;
public:
void ChangeToOwnerDraw(CMyMenu* pMyMenu);
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
private:
vector<CMyMenu*> rgpMyMenu;
vector<CMyMenuData*> rgpMyMenuData;
We have to add the implementation in MyMenu.cpp, as shown below.
void CMyMenu::ChangeToOwnerDraw(CMyMenu *pMyMenu)
{
CString str;
CMyMenu* pMenu;
CMyMenuData* pMenuData;
int iMenuCount = pMyMenu->GetMenuItemCount();
UINT nID;
for (int i=0; i<iMenuCount; i++)
{
pMyMenu->GetMenuString(i, str, MF_BYPOSITION);
pMenu = 0;
pMenuData = 0;
pMenuData = new CMyMenuData;
pMenuData->m_strCaption = str;
rgpMyMenuData.push_back(pMenuData);
if (pMyMenu->GetSubMenu(i))
{
pMyMenu->ModifyMenu(i,
MF_BYPOSITION | MF_OWNERDRAW,
0,
(LPCTSTR)pMenuData);
pMenu = new CMyMenu;
rgpMyMenu.push_back(pMenu);
HMENU hMenu = pMyMenu->GetSubMenu(i)->GetSafeHmenu();
pMenu->Attach(hMenu);
ChangeToOwnerDraw(pMenu);
}
else
{
nID = pMyMenu->GetMenuItemID(i);
pMyMenu->ModifyMenu(i,
MF_BYPOSITION | MF_OWNERDRAW,
(UINT)nID,
(LPCTSTR)pMenuData);
}
}
}
void CMyMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
CMyMenuData* pMyMenuData =
(CMyMenuData*)lpMeasureItemStruct->itemData;
CString str = pMyMenuData->m_strCaption;
lpMeasureItemStruct->itemHeight = 23;0
lpMeasureItemStruct->itemWidth = str.GetLength()*7;
}
void CMyMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
pDC->FillSolidRect(&lpDrawItemStruct->rcItem,
RGB(255,150,0));
if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
pDC->FillSolidRect(&lpDrawItemStruct->rcItem,
RGB(0,150,255));
}
CMyMenuData* pMyMenuData =
(CMyMenuData*)lpDrawItemStruct->itemData;
CString str = pMyMenuData->m_strCaption;
pDC->TextOut(lpDrawItemStruct->rcItem.left,
lpDrawItemStruct->rcItem.top, str);
}
CMyMenu::~CMyMenu()
{
int iCount = rgpMyMenu.size();
for (int i=0; i<iCount; i++)
{
rgpMyMenu[i]->Detach();
delete rgpMyMenu[i];
rgpMyMenu[i] = 0;
}
iCount = rgpMyMenuData.size();
for (i=0; i<iCount; i++)
{
delete rgpMyMenuData[i];
rgpMyMenuData[i] = 0;
}
}
After we have added a lot of things to CMyMenu class, it’s the time to use it in the CMainFrame class. Add the following to the MainFrm.h.
#include “MyMenu.h”
private:
CMyMenu* pMyMenu;
At the end of the CMainFrame::OnCreate function, but before the line “return 0;”, add the following code.
pMyMenu = new CMyMenu;
HMENU hMenu = ::GetMenu(GetSafeHwnd());
pMyMenu->Attach(hMenu);
SetMenu(pMyMenu);
pMyMenu->ChangeToOwnerDraw(pMyMenu);
We have to add a message handler for OnDestory, too. Right-click CMainFrame in the ClassView; choose “Add Windows Message Handler.” Add WM_DESTORY handler. The code of the OnDestroy function is as the following.
void CMainFrame::OnDestroy()
{
CFrameWnd::OnDestroy();
if (pMyMenu)
{
pMyMenu->Detach();
delete pMyMenu;
pMyMenu = 0;
}
}
We have finished our long adventure. Press F7 for our final fire.
Downloads
Download demo project – 33 Kb