MFC中实现自绘菜单

转自:http://blog.csdn.net/tg2003/archive/2009/04/26/4126337.aspx

 

为了实现菜单的自绘,花了我几个小时,其实真正解决后又发现很简单。实现菜单的自绘只需要三个步骤:

第一步:将所有菜单项设置为MF_OWNERDRAW,即自绘模式

第二步:在WM_MEASUREITEM消息中设置菜单项的大小

第三步:在WM_DRAWITEM消息中进行菜单项的绘制

问题首先出现在了第一步,我要绘制的是一个上下文菜单,即右键菜单,要将菜单项设置为MF_OWNERDRAW,需要用到ModifyMenu函数,起始由于ModifyMenu函数的参数设置错误,导致程序怎么也响应不了WM_MEASUREITEM和WM_DRAWITEM消息,所以建议在使用ModifyMenu时对返回值进行检查。

view plaincopy to clipboardprint?
void CMainWindow::OnRButtonDown(UINT nFlags, CPoint point)  
{  
    ClientToScreen(&point);  
    CMenu Menu;  
    Menu.LoadMenuW(IDR_MENU2);  
    CMenu *pMenu = Menu.GetSubMenu(0);  
    CString strText;  
    for (int i = 0; i < pMenu->GetMenuItemCount(); i++)  
    {  
        BOOL bModi = pMenu->ModifyMenuW(ID_123_456 + i, MF_BYCOMMAND|MF_OWNERDRAW, ID_123_456 + i);  
        if (!bModi)  
       {  
              TRACK("ModifyMenu fail!");  
       }  
        pMenu->GetMenuStringW(i, strText, MF_BYPOSITION);  
    }  
    pMenu->TrackPopupMenu(TPM_LEFTBUTTON|TPM_LEFTALIGN,  
        point.x,  
        point.y,  
        this);  

void CMainWindow::OnRButtonDown(UINT nFlags, CPoint point)
{
    ClientToScreen(&point);
    CMenu Menu;
    Menu.LoadMenuW(IDR_MENU2);
    CMenu *pMenu = Menu.GetSubMenu(0);
    CString strText;
    for (int i = 0; i < pMenu->GetMenuItemCount(); i++)
    {
        BOOL bModi = pMenu->ModifyMenuW(ID_123_456 + i, MF_BYCOMMAND|MF_OWNERDRAW, ID_123_456 + i);
        if (!bModi)
       {
              TRACK("ModifyMenu fail!");
       }
        pMenu->GetMenuStringW(i, strText, MF_BYPOSITION);
    }
    pMenu->TrackPopupMenu(TPM_LEFTBUTTON|TPM_LEFTALIGN,
        point.x,
        point.y,
        this);
}

第一步的问题解决后,接在在第二步的WM_MEASUREITEM消息中设置菜单项的大小:

view plaincopy to clipboardprint?
void CMainWindow::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis)  
{  
    //lpmis->itemWidth = ::GetSystemMetrics(SM_CYMENU) * 4;  
    lpmis->itemWidth = 150;  
    lpmis->itemHeight = ::GetSystemMetrics(SM_CYMENU);  

void CMainWindow::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis)
{
    //lpmis->itemWidth = ::GetSystemMetrics(SM_CYMENU) * 4;
    lpmis->itemWidth = 150;
    lpmis->itemHeight = ::GetSystemMetrics(SM_CYMENU);
}
在WM_MEASUREITEM消息中设置的菜单项大小会传入WM_DRAWITEM消息中,然后再在WM_DRAWITEM消息中根据菜单项的大小来进行重绘。

到第三步也遇到了几个问题,由于最初对WM_DRAWITEM消息中的LPDRAWITEMSTRUCT结构体不了解,以致写出的程序不管在什么时候都会作同一个绘制操作,先来看看WM_DRAWITEM消息的声明:

afx_msg void CMainWindow::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis);

在这个消息中有两个参数,在自绘菜单时,两个参数都要用到。其中nIDCtl,书上说是所属控件的ID,不太明白是什么意思,在MSDN看到,对于菜单发出的WM_DRAWITEM消息,nIDCtl为0。再说LPDRAWITEMSTRUCT结构体,该结构体中包含了菜单复选状态、选中状态以及菜单项的大小等信息。

下面是OnDrawItem消息的实现代码:

view plaincopy to clipboardprint?
void CMainWindow::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis)  
{  
    CBrush *brush = new CBrush;  
    CPen *pen = new CPen;  
    CString strText;  
    CDC *pDC = CDC::FromHandle(lpdis->hDC); //获取菜单项的设备句柄  
    //菜单项是否为选中状态  
    if ((lpdis->itemState & ODS_SELECTED))  
    {  
        //在菜单项上自绘矩形框的背景颜色  
        brush->CreateSolidBrush(RGB(182, 189,210));  
        //在菜单项自绘矩形的边框颜色  
        pen->CreatePen(PS_SOLID, 1, RGB(10,36,106));  
        //设置菜单项的文字背景颜色  
        pDC->SetBkColor(RGB(182,189,210));  
    }  
    else 
    {  
        brush->CreateSolidBrush(GetSysColor(COLOR_MENU));  
        pen->CreatePen(PS_SOLID, 0, GetSysColor(COLOR_MENU));  
        pDC->SetBkColor(GetSysColor(COLOR_MENU));  
    }  
    pDC->SelectObject(pen);  
    pDC->SelectObject(brush);  
    //在当前菜单项上画一个矩形框  
    pDC->Rectangle(lpdis->rcItem.left,  
        lpdis->rcItem.top,  
        lpdis->rcItem.right,  
        lpdis->rcItem.bottom);  
    /*--------------------------------------*/ 
    //获取当前消息所在菜单项的文本  
    CMenu menu;  
    menu.Attach((HMENU)lpdis->hwndItem);  
    menu.GetMenuStringW(lpdis->itemID, strText,MF_BYCOMMAND);  
    /*--------------------------------------*/ 
    //如果为菜单发出的DrawItem消息  
    if (nIDCtl == 0)  
    {  
        //在菜单项上输出菜单文本  
        pDC->TextOutW(lpdis->rcItem.left + 20, lpdis->rcItem.top + 4, strText.GetBuffer(0), strText.GetLength());  
    }  
    menu.Detach();  
    delete brush;  
    delete pen;  

void CMainWindow::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis)
{
    CBrush *brush = new CBrush;
    CPen *pen = new CPen;
    CString strText;
    CDC *pDC = CDC::FromHandle(lpdis->hDC); //获取菜单项的设备句柄
    //菜单项是否为选中状态
    if ((lpdis->itemState & ODS_SELECTED))
    {
        //在菜单项上自绘矩形框的背景颜色
        brush->CreateSolidBrush(RGB(182, 189,210));
        //在菜单项自绘矩形的边框颜色
        pen->CreatePen(PS_SOLID, 1, RGB(10,36,106));
        //设置菜单项的文字背景颜色
        pDC->SetBkColor(RGB(182,189,210));
    }
    else
    {
        brush->CreateSolidBrush(GetSysColor(COLOR_MENU));
        pen->CreatePen(PS_SOLID, 0, GetSysColor(COLOR_MENU));
        pDC->SetBkColor(GetSysColor(COLOR_MENU));
    }
    pDC->SelectObject(pen);
    pDC->SelectObject(brush);
    //在当前菜单项上画一个矩形框
    pDC->Rectangle(lpdis->rcItem.left,
        lpdis->rcItem.top,
        lpdis->rcItem.right,
        lpdis->rcItem.bottom);
    /*--------------------------------------*/
    //获取当前消息所在菜单项的文本
    CMenu menu;
    menu.Attach((HMENU)lpdis->hwndItem);
    menu.GetMenuStringW(lpdis->itemID, strText,MF_BYCOMMAND);
    /*--------------------------------------*/
    //如果为菜单发出的DrawItem消息
    if (nIDCtl == 0)
    {
        //在菜单项上输出菜单文本
        pDC->TextOutW(lpdis->rcItem.left + 20, lpdis->rcItem.top + 4, strText.GetBuffer(0), strText.GetLength());
    }
    menu.Detach();
    delete brush;
    delete pen;
}

效果图:

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tg2003/archive/2009/04/26/4126337.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值