程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 三個函數實現框架菜單自繪

三個函數實現框架菜單自繪

編輯:vc教程

  在VCKBASE看到的自繪菜單都是派生出一個新類,其實不用這麼麻煩,添加三個函數即可實現框架菜單自繪,方便簡單,易於維護。

  在MFC中,如果菜單帶有MF_OWNERDRAW標志,程序就會調用OnDrawItem和OnMeasureItem函數來繪制菜單。

  下面就讓我們來動手吧!首先在CMainFrame響應三個消息,分別是:

WM_DRAWITEM:繪制菜單的樣式
WM_MEASUREITEM:指定要繪制菜單的大小
WM_INITMENU:把框架菜單全部改成帶MF_OWNERDRAW標志

  下面我帖出這三個函數代碼,你不想改的話,把這三個函數的代碼復制你的程序,編譯一下看看你的程序菜單是不是變得很漂亮:)

void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
  LPMEASUREITEMSTRUCT& lpM=lpMeasureItemStruct; //起個別名,好用一點
  if(lpM->CtlType==ODT_MENU){       //判斷是不是菜單要自繪
   if(lpM->itemID!=ID_SEPARATOR)   //分別設定普通菜單和分隔欄的大小  
   {
      lpM->itemHeight=20;  //分隔欄大小    
      lpM->itemWidth=150;  
   }  
   else  
    {    
      lpM->itemHeight=1;   //普通菜單大小    
      lpM->itemWidth=150;  
    }
  }
}
void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  LPDRAWITEMSTRUCT& lpD=lpDrawItemStruct; //起個別名,好用一點
  //判斷是不是菜單自繪,因為按鈕也可以自繪
  if(nIDCtl==0)              
  {
    CDC* pDC=CDC::FromHandle(lpD->hDC); //得到菜單的設備指針,用來繪制菜單
    pDC->SetBkMode(TRANSPARENT);
    CMenu menu;
    menu.Attach((HMENU)lpD->hwndItem);  //得到框架菜單對象
    CRect AllRgn(lpD->rcItem);       //得到當前繪制的菜單選項項大小
    CRect FrontRgn(AllRgn.left,AllRgn.top,20,AllRgn.bottom);
    CBrush brushAll(RGB( 250,250,250 ));   //初始化畫刷
    CBrush brushFront(RGB( 230,230,230 ));
    CBrush brushSel(RGB(148,170,214 ));
    CString strText;
     menu.GetMenuString(lpD->itemID,strText,MF_BYCOMMAND); //得到當前繪制的菜單選項文本
 
     if(lpD->itemID!=ID_SEPARATOR)  //菜單和分隔欄分別繪制
     {
         if(lpD->itemAction & ODA_SELECT) //菜單選中時的樣式
         {
         pDC->FillRect(AllRgn,&brushSel);  //繪制
         if(lpD->itemState &ODS_GRAYED)
          //設定文本顏色(在最後才繪制出來)
           pDC->SetTextColor(RGB(194,194,194));
         else if(lpD->itemState & ODS_SELECTED)
           pDC->SetTextColor(RGB(250,250,250));
       }
      //菜單非選中時的樣式
       if(!((lpD->itemAction & ODA_SELECT) && (lpD->itemState & ODS_SELECTED))) 
       {  
         pDC->FillRect(AllRgn,&brushAll);  //繪制
         pDC->FillRect(FrontRgn,&brushFront);
         if(lpD->itemState & ODS_GRAYED)
           pDC->SetTextColor(RGB(194,194,194 ));
         else
          pDC->SetTextColor(RGB(66,110,180 ));
       }  
    }
    else
       pDC->FillRect(AllRgn,&brushFront); //繪制分隔欄
    pDC->TextOut(AllRgn.left+30,AllRgn.top+5,strText); //打印出字體
    menu.Detach();//分隔菜單句柄和對象(必要!)
  }
}
void CMainFrame::OnInitMenu(CMenu* pMenu)
{
   CMenu *pSubMenu;
   UINT nCount,nSubCount,nID;
   CString strText;
    nCount=pMenu->GetMenuItemCount();
   for(UINT i=0;i<nCount;i++)      
   {
     pSubMenu =pMenu->GetSubMenu(i);
     nSubCount=pSubMenu->GetMenuItemCount();
     for(UINT j=0;j<nSubCount;j++)
     {
       nID=pSubMenu->GetMenuItemID(j);
      //將框架菜單所有菜單都添加MF_OWNERDRAW標志
       pSubMenu->ModifyMenu(j,MF_BYPOSITION|MF_OWNERDRAW,nID); 
       pSubMenu->GetMenuString(j,strText,MF_BYPOSITION);
     }  
   }
}    

  這樣,你的程序就擁有了一個漂亮的菜單:) 這只是個初板而已。

  建議改進:因為每次彈出菜單的時候都調用OnInitMenu,本來已改好的菜單就不必再改了,在OnInitMenu加一個全部變量標識菜單是否改好了,避免重復的修改菜單。那當然也可以在OnCreate中修改,不過你要確定你的菜單沒有再添加新選項了。

  缺點:不清楚為什麼對"最近文件"那項不起作用,知道的還望告訴我一下。對子菜單的彈出菜單沒有修改MF_OWNERDRAW,不過你可以增加一點代碼遍歷一下就OK了。這樣一個簡單菜單換膚就完成了,^_^

  (參考了VCK的一些資料)

  本文參考了 VCKBASE 的一些資料,以及 MSDN 庫:只列出部分

void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
lpMeasureItemStruct 是指向MEASUREITEMSTRUCT結構體的指針,其成員變量
UINT CtlType; // 要繪制的類型
UINT itemID; // 菜單選項ID
UINT itemWidth; //菜單選項寬度
UINT itemHeight; //菜單選項高度
void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
lpDrawItemStruct 是指向DRAWITEMSTRUCT結構體的指針,其成員變量
UINT CtlType; // 要繪制的類型
UINT itemID; // 菜單選項ID
UINT itemAction; // 菜單動作
UINT itemState; // 菜單選項的當前狀態
HWND hwndItem; // 頂層菜單的句柄
HDC hDC; // 繪制設備DC
RECT rcItem; // 菜單選項的大小
DWORD itemData; // 附加自定義數據,由AppendMenu或InsertMenu或ModifyMenu的lpszNewItem指定

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved