源代码:http://download.csdn.net/source/3105184工作时遇到对话框上的LISTCTRL的右键菜单想要美化一下,然后看了一些网上的美化菜单类,自己做了一个适合自己的。包含头文件后只需要在CMainFrame的OnCreate 或 CDialog的OnInitDialog 函数中加入代码 CMenuEx::SubclassMenu( this ); 即可。没有非常繁杂的在这里那里加入代码的操作。可以美化单文档,对话框,对话框上的LISTCTRL的右键菜单等。多文档存在一些问题,我看过网上的其它的MENU美化类,对多文档也存在一些BUG。没有深入的研究,能够满足我平常的应用了。代码比较简单,初学者看看应该有好处。这里贴图太麻烦,就不帖图了。平台是VS 2008的。

解决方案 »

  1.   

    再帖上代码吧。
    这是头文件:
    #define CMenuEx_Window_Prop_Name _T("CMenuEx_Window_Prop_Name")class CMenuEx
    {
    public:
    CMenuEx();
    virtual ~CMenuEx();public:
    static CMenuEx * SubclassMenu( CWnd * pWnd ); void InitFrameMenu( CWnd * pWnd );
    void SetImageList( UINT nBitmapResourceID,COLORREF crMask,int cx,int cy,UINT * nResourceIDArray,UINT nResourceIDCount );public:
    void OnInitMenu( CWnd * pWnd,CMenu* pMenu );
    void OnInitMenuPopup( CWnd * pWnd,CMenu* pMenu,UINT nIndex,BOOL bSysMenu ); void OnMeasureItem( CWnd * pWnd,int nIDCtl,LPMEASUREITEMSTRUCT lpMeasureItemStruct );
    void OnDrawItem( CWnd * pWnd,int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct );protected:
    void ModifyMenuStyle( CMenu* pMenu,UINT nItem,BOOL bByPos,BOOL bFrameMenu = FALSE );
    void EnumMenus( CMenu * pMenu ); static LRESULT CALLBACK WindowProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam );protected:
    CBitmap m_Bitmap;//设定为成员变量的原因:The application must call the DeleteObject function to delete each bitmap handle returned by the LoadBitmap function. 
    CImageList * m_pImageList;
    CUIntArray m_nResourceIDArray; CMap< HWND,HWND,WNDPROC,WNDPROC > m_mapWndProc;
    };
      

  2.   

    这是源文件:// MenuEx.cpp : 实现文件
    //#include "stdafx.h"
    #include "MenuEx.h"
    // CMenuExCMenuEx::CMenuEx()
    {
    m_pImageList = NULL;
    }CMenuEx::~CMenuEx()
    {
    if( m_pImageList )
    {
    m_pImageList->DeleteImageList();
    delete m_pImageList;
    m_pImageList = NULL;
    }
    m_Bitmap.DeleteObject();
    }CMenuEx * CMenuEx::SubclassMenu( CWnd * pWnd )
    {
    if( pWnd && pWnd->GetSafeHwnd() && IsWindow( pWnd->m_hWnd ) )
    {
    static CMenuEx menu; WNDPROC wndProcPrev = NULL;
    if( menu.m_mapWndProc.Lookup( pWnd->m_hWnd,wndProcPrev ) )//此窗口菜单已经被设置自绘则返回
    {
    return &menu;
    } wndProcPrev = (WNDPROC)::SetWindowLong( pWnd->m_hWnd,GWL_WNDPROC,(LONG)WindowProc );
    menu.m_mapWndProc.SetAt( pWnd->m_hWnd,wndProcPrev );
    if( SetProp( pWnd->m_hWnd,CMenuEx_Window_Prop_Name,(HANDLE)(&menu) ) )
    {
    return &menu;
    }
    else
    {
    ::SetWindowLong( pWnd->m_hWnd,GWL_WNDPROC,(LONG)wndProcPrev );
    return NULL;
    }
    }
    return NULL;
    }void CMenuEx::ModifyMenuStyle( CMenu* pMenu,UINT nItem,BOOL bByPos,BOOL bFrameMenu/* = FALSE*/ )
    {
    if( pMenu && pMenu->GetSafeHmenu() && IsMenu( pMenu->m_hMenu ) )
    {
    MENUITEMINFO MenuItemInfo;
    memset( &MenuItemInfo,0,sizeof(MenuItemInfo) );
    MenuItemInfo.cbSize = sizeof(MENUITEMINFO);
    MenuItemInfo.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA; if( pMenu->GetMenuItemInfo( nItem,&MenuItemInfo,bByPos ) )
    {
    MenuItemInfo.fType |= MF_OWNERDRAW;
    if( MenuItemInfo.dwItemData == 0 )
    {
    MenuItemInfo.dwItemData = (DWORD)(pMenu->GetSafeHmenu());
    if( bFrameMenu )
    {
    //菜单项的句柄是int型,int型的最高位用来表示int的正负,但菜单项句柄不会为负数,所以用这里的最高位表示此菜单项是否是顶层框架菜单项
    MenuItemInfo.dwItemData |= 0x80000000;//或者 MenuItemInfo.dwItemData |= 0x00000001 << 31 即二进制的 1000 0000 0000 0000 0000 0000 0000 0000;
    }
    } pMenu->SetMenuItemInfo( nItem,&MenuItemInfo,bByPos );
    }
    }
    }void CMenuEx::InitFrameMenu( CWnd * pWnd )
    {
    if( pWnd && pWnd->GetSafeHwnd() )
    {
    CMenu * pMenu = pWnd->GetMenu();
    if( pMenu && pMenu->GetSafeHmenu() )
    {
    int i,nCount = pMenu->GetMenuItemCount();
    for( i = 0; i < nCount; i++ )
    {
    ModifyMenuStyle( pMenu,i,TRUE,TRUE );
    }
    }
    }
    }void CMenuEx::OnInitMenu( CWnd * pWnd,CMenu* pMenu )
    {
    if( pWnd->IsIconic() )
    return;
    EnumMenus( pMenu );
    }void CMenuEx::OnInitMenuPopup( CWnd * pWnd,CMenu* pMenu,UINT nIndex,BOOL bSysMenu )
    {
    if( !bSysMenu )//为“最近的文件”菜单项添加自绘标志
    {
    int i,nID,nCount = pMenu->GetMenuItemCount();
    for( i = 0; i < nCount; i++ )
    {
    nID = pMenu->GetMenuItemID( i );
    if( nID >= ID_FILE_MRU_FIRST && nID <= ID_FILE_MRU_LAST )
    {
    ModifyMenuStyle( pMenu,i,TRUE );
    }
    }
    }
    }void CMenuEx::EnumMenus( CMenu * pMenu )
    {
    if( pMenu && pMenu->GetSafeHmenu() )
    {
    int i,nCount = pMenu->GetMenuItemCount();
    for( i = 0; i < nCount; i++ )
    {
    ModifyMenuStyle( pMenu,i,TRUE ); EnumMenus( pMenu->GetSubMenu( i ) );
    }
    }
    }void CMenuEx::OnMeasureItem( CWnd * pWnd,int nIDCtl,LPMEASUREITEMSTRUCT lpMeasureItemStruct )
    {
    if( lpMeasureItemStruct->CtlType == ODT_MENU )//判断是不是菜单要自绘
    {
    //OnMeasureItem函数对每个菜单项只会调用一次,下次再次弹出菜单式此函数不会再次调用
    if( lpMeasureItemStruct->itemID != ID_SEPARATOR )//
    {
    CMenu * pMenu = CMenu::FromHandle( (HMENU)(lpMeasureItemStruct->itemData & 0x7FFFFFFF) );
    if( pMenu && pMenu->GetSafeHmenu() && IsMenu( pMenu->m_hMenu ) )
    {
    CDC * pDC = pWnd->GetDC();
    if( pDC )
    {
    CString strText;
    pMenu->GetMenuString( lpMeasureItemStruct->itemID,strText,MF_BYCOMMAND );
    CSize size = pDC->GetTabbedTextExtent( strText,0,NULL );
    if( lpMeasureItemStruct->itemData & 0x80000000 )//顶层框架菜单项,如“文件”“编辑”等
    lpMeasureItemStruct->itemWidth = size.cx - 8;
    else
    lpMeasureItemStruct->itemWidth = size.cx + 32;//普通菜单项 宽度 所有菜单项都设置宽度后,显示时会自动扩展到以最长宽度为准
    pWnd->ReleaseDC( pDC );
    }
    }
    lpMeasureItemStruct->itemHeight = 24;//普通菜单高度
    }
    else
    {
    lpMeasureItemStruct->itemHeight = 8;//分隔栏高度
    lpMeasureItemStruct->itemWidth = 1;//分隔栏宽度
    }
    }
    }void CMenuEx::SetImageList( UINT nBitmapResourceID,COLORREF crMask,int cx,int cy,UINT * nResourceIDArray,UINT nResourceIDCount )
    {
    if( m_pImageList == NULL )
    {
    m_pImageList = new CImageList();
    }
    m_pImageList->DeleteImageList();
    m_nResourceIDArray.RemoveAll();
    UINT i;
    for( i = 0; i < nResourceIDCount; i++ )
    {
    m_nResourceIDArray.Add( nResourceIDArray[i] );
    }
    if( m_pImageList->Create( cx,cy,ILC_MASK | ILC_COLOR24,8,1 ) )
    {
    m_Bitmap.DeleteObject();
    m_Bitmap.LoadBitmap( nBitmapResourceID );
    m_pImageList->Add( &m_Bitmap,crMask );
    }
    }
      

  3.   

    void CMenuEx::OnDrawItem( CWnd * pWnd,int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct )
    {
    //判断是不是菜单自绘,因为按钮也可以自绘
    if( nIDCtl == 0 )//If a menu sent the message, nIDCtl contains 0.
    {
    CDC* pDC = CDC::FromHandle( lpDrawItemStruct->hDC );//得到菜单设备指针,用来绘制菜单字串
    if( pDC )
    {
    int nSavedDC = pDC->SaveDC(); CRect rcItem( lpDrawItemStruct->rcItem );//得到当前绘制菜单选项项大小
    CRect rcLeftSpace( rcItem.left,rcItem.top,rcItem.left + 24,rcItem.bottom );
    CRect rcRightSpace( rcItem.left + 32,rcItem.top,rcItem.right,rcItem.bottom ); if( lpDrawItemStruct->itemID != ID_SEPARATOR ) //菜单和分隔栏分别绘制
    {
    CMenu * pMenu = CMenu::FromHandle( (HMENU)lpDrawItemStruct->hwndItem );//
    if( pMenu && pMenu->GetSafeHmenu() && IsMenu( pMenu->m_hMenu ) )
    {
    pDC->SetBkMode( TRANSPARENT ); CString strText;
    UINT uFormat;
    pMenu->GetMenuString( lpDrawItemStruct->itemID,strText,MF_BYCOMMAND );//得到当前绘制菜单选项文本
    if( lpDrawItemStruct->itemData & 0x80000000 )//顶层框架菜单项,如“文件”“编辑”等
    {
    CRect rcFrameMenuItem( rcItem );
    rcFrameMenuItem.InflateRect( -1,-1 );
    pDC->FillSolidRect( rcFrameMenuItem,GetSysColor( COLOR_3DFACE ) );//用指定色填充背景
    if( lpDrawItemStruct->itemState & ODS_SELECTED )
    {
    pDC->FillSolidRect( rcFrameMenuItem,0xffffff ); //绘制
    pDC->FrameRect( rcFrameMenuItem,&CBrush( 0xC56A31 ) );
    }
    else if( lpDrawItemStruct->itemState & ODS_HOTLIGHT )
    {
    pDC->FillSolidRect( rcFrameMenuItem,0xEDD2C1 ); //绘制
    pDC->FrameRect( rcFrameMenuItem,&CBrush( 0xC56A31 ) );
    }
    pDC->SetTextColor( ( lpDrawItemStruct->itemState & ODS_GRAYED ) ? 0x99A8AC : 0x000000 );
    uFormat = DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_SINGLELINE | DT_EXPANDTABS | DT_TABSTOP;
    pDC->DrawText( strText,rcFrameMenuItem,uFormat );//打印出字体
    }
    else
    {
    pDC->FillSolidRect( rcItem,0xF9F9F9 );//用白色填充背景
    pDC->FillSolidRect( rcLeftSpace,0xD8E9ED );//在左侧画阴影
    if( lpDrawItemStruct->itemState & ODS_SELECTED )//菜单处于选中状态
    {
    if( !( lpDrawItemStruct->itemState & ODS_GRAYED ) )//不是灰色菜单
    {
    pDC->FillSolidRect( rcItem,0xEDD2C1 ); //绘制
    pDC->FrameRect( rcItem,&CBrush( 0xC56A31 ) );
    }
    }
    if( lpDrawItemStruct->itemState & ODS_CHECKED )//
    {
    CRect rcCheck( rcLeftSpace );
    rcCheck.InflateRect( -2,-2 );
    if( lpDrawItemStruct->itemState & ODS_SELECTED )//菜单处于选中状态
    pDC->FillSolidRect( rcCheck,0xE4BBA2 );
    else
    pDC->FillSolidRect( rcCheck,0xF6E8E0 );
    pDC->FrameRect( rcCheck,&CBrush( 0xC56A31 ) );
    uFormat = DT_CENTER | DT_VCENTER | DT_SINGLELINE;
    pDC->DrawText( _T("√"),rcCheck,uFormat );//打印出字体
    }
    //画图标
    if( m_pImageList != NULL )
    {
    int i,nCount = m_nResourceIDArray.GetCount();
    for( i = 0; i < nCount; i++ )
    {
    if( lpDrawItemStruct->itemID == m_nResourceIDArray.GetAt( i ) && m_pImageList->GetImageCount() > i )
    {
    IMAGEINFO ImageInfo;
    memset( &ImageInfo,0,sizeof( IMAGEINFO ) );
    m_pImageList->GetImageInfo( i,&ImageInfo );
    CRect rcBitmap( rcLeftSpace );
    rcBitmap.left += ( rcBitmap.Width() - ( ImageInfo.rcImage.right - ImageInfo.rcImage.left ) + 1 ) / 2;
    rcBitmap.top += ( rcBitmap.Height() - ( ImageInfo.rcImage.bottom - ImageInfo.rcImage.top ) + 1 ) / 2;
    if( ( lpDrawItemStruct->itemState & ODS_SELECTED ) && !( lpDrawItemStruct->itemState & ODS_GRAYED ) )//菜单处于选中状态
    {
    rcBitmap.left -= 1;
    rcBitmap.top -= 1;
    }
    m_pImageList->Draw( pDC,i,rcBitmap.TopLeft(),ILD_TRANSPARENT );
    }
    }
    }
    //写文字
    CString strLeft( strText ),strRight;
    int nTabIndex = strText.Find('\t');
    if( nTabIndex > 0 )
    {
    strLeft = strText.Left( nTabIndex );
    strRight = strText.Right( strText.GetLength() - nTabIndex - 1 );
    }
    pDC->SetTextColor( ( lpDrawItemStruct->itemState & ODS_GRAYED ) ? 0x99A8AC : 0x000000 );
    uFormat = ( 10 << 8 ) | DT_VCENTER | DT_WORDBREAK | DT_SINGLELINE | DT_EXPANDTABS | DT_TABSTOP;
    pDC->DrawText( strLeft,rcRightSpace,uFormat );//打印出字体
    if( !strRight.IsEmpty() )
    {
    CRect rcRightText( rcRightSpace.left,rcRightSpace.top,rcRightSpace.right - 16,rcRightSpace.bottom );
    uFormat = DT_RIGHT | DT_VCENTER | DT_WORDBREAK | DT_SINGLELINE | DT_EXPANDTABS | DT_TABSTOP;
    pDC->DrawText( strRight,rcRightText,uFormat );//打印出字体
    }
    }
    }
    }
    else
    {
    pDC->FillSolidRect( rcItem,0xF9F9F9 );//用白色填充背景
    pDC->FillSolidRect( rcLeftSpace,0xD8E9ED );//在左侧画阴影
    CRect rcSeparator( rcRightSpace.left,rcRightSpace.top + rcRightSpace.Height() / 2,rcRightSpace.right,rcRightSpace.top + rcRightSpace.Height() / 2 + 1 );
    pDC->FillRect( rcSeparator,&CBrush( 0x99A8AC ) );//绘制分隔栏
    }
    pDC->RestoreDC( nSavedDC );
    }
    }
    }LRESULT CALLBACK CMenuEx::WindowProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam )
    {
    LRESULT lResult = 0;
    CMenuEx * pMenu = (CMenuEx *)GetProp( hWnd,CMenuEx_Window_Prop_Name );
    if( pMenu )
    {
    WNDPROC wndProcPrev = NULL;
    if( pMenu->m_mapWndProc.Lookup( hWnd,wndProcPrev ) )
    {
    switch(uMsg)
    {
    case WM_INITMENU:
    lResult = CallWindowProc( wndProcPrev,hWnd,uMsg,wParam,lParam );
    pMenu->OnInitMenu( CWnd::FromHandle( hWnd ),CMenu::FromHandle( (HMENU)wParam ) );
    return lResult;
    case WM_INITMENUPOPUP:
    lResult = CallWindowProc( wndProcPrev,hWnd,uMsg,wParam,lParam );
    pMenu->OnInitMenuPopup( CWnd::FromHandle( hWnd ),CMenu::FromHandle( (HMENU)wParam ),LOWORD( lParam ),HIWORD( lParam ) );
    return lResult;
    case WM_MEASUREITEM:
    pMenu->OnMeasureItem( CWnd::FromHandle( hWnd ),wParam,(LPMEASUREITEMSTRUCT)lParam );
    break;
    case WM_DRAWITEM:
    pMenu->OnDrawItem( CWnd::FromHandle( hWnd ),wParam,(LPDRAWITEMSTRUCT)lParam );
    break;
    case WM_NCDESTROY:
    SetWindowLong( hWnd,GWL_WNDPROC,(LONG)wndProcPrev );//设置回以前的窗口过程
    RemoveProp( hWnd,CMenuEx_Window_Prop_Name );//移除窗口标记
    pMenu->m_mapWndProc.RemoveKey( hWnd );//移除 窗口句柄-窗口过程 MAP对
    break;
    default:
    break;
    }
    return CallWindowProc( wndProcPrev,hWnd,uMsg,wParam,lParam );
    }
    }
    return 0L;
    }