改變菜單欄字體的大小

這兩天在菜單欄,需要改變菜單欄字體的大小,但是CMenu類沒有提供相應的方法,自己上網找了點,找到了兩種方法,具體的使用情況還有待研究,先做點筆記吧

方法一:在MainFrame的OnCreate函數中增加如下代碼

// start
	LOGFONT m_lf;  
	memset(&m_lf, 0, sizeof(LOGFONT));     

	m_lf.lfHeight = 26; 
	m_lf.lfWeight = 700;//設置字體爲粗體(一般爲400,粗體爲700)

	_tcsncpy_s(m_lf.lfFaceName, LF_FACESIZE, _T("Arial"), 7); //字符拷貝函數,使用的如果是UNICODE編碼,則採用wcscpy_s()函數,如果是多字節編碼,則採用strcpy_s()函數 ,m_lf.lfFaceName設置字體名稱
	m_wndMenuBar.SetMenuFont(&m_lf); 
	// end

或者這麼寫:

        LOGFONT logfont = {0};
	::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logfont, 0);
	logfont.lfHeight = 26;
	logfont.lfWeight = 700;
	afxGlobalData.SetMenuFont(&logfont, true);
這裏似乎只能改變主窗口的菜單字體,並且主窗口其他地方的體統字體都會隨之改變。那麼如果在一個彈出對話框中,要改變其菜單的字體如何實現呢?請看方法二


方法二:

首先從CMenu派生出一個子類CNewMenu(利用嚮導建立新的MFC類時,選擇基類時沒有CMenu,而CMenu派生自CObject,所以可以先將CNewMenu派生自CObject,然後再聲明和定義的地方將CObject改成CMenu),然後往這個類添加三個成員函數,MeasureItem(設置菜單寬高),
DrawItem(自繪菜單),ChangeMenuItem(修改菜單項類型)

三個函數分別定義如下:

void CNewMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)

void CNewMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

void CNewMenu::ChangeMenuItem(CMenu *pMenu)

其中MeasureItem和DrawItem是CMenu類的虛函數。

各函數的代碼如下:

void CNewMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
 lpMeasureItemStruct->itemHeight=25;//項高
 lpMeasureItemStruct->itemWidth=120;//項寬
}


void CNewMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{

	// TODO:  Add your code to draw the specified item
	CRect rect=lpDrawItemStruct->rcItem;//菜單項矩形區域
	CRect rectCheck=lpDrawItemStruct->rcItem;
	rectCheck.right = rectCheck.left + 25;//icon或checkmark的矩形區域
	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);
	dc.FillSolidRect(rect,/*RGB(255,255,255)*/dc.GetBkColor());//設置菜單項的文本背景色
	dc.DrawEdge(&rect, EDGE_ETCHED, BF_TOP);//繪製菜單項邊界
	
	
	CFont Font;
	Font.CreatePointFont(125,"宋體");//創建字體
	dc.SelectObject(&Font);
	CString *pText=(CString *)lpDrawItemStruct->itemData;
	rect.left += 25;
	if(lpDrawItemStruct->itemState&ODS_SELECTED)
	{
		dc.FillSolidRect(rect,RGB(0,255,0));//設置菜單被選中時的背景色
	}
	dc.SetTextColor(RGB(10,0,181));//設置文本顏色
	dc.DrawText(*pText,rect,DT_VCENTER|DT_LEFT|DT_SINGLELINE);//繪製文字
	
	
	//通過菜單項ID來確定是否顯示icon以及是否checked
	switch(lpDrawItemStruct->itemID)
	{
	case IDM_MENU21:
		//DrawMenuItemIcon(&dc,rectCheck,FALSE/*,IDB_BITMAP1*/);
		DrawCheckMark(&dc,rectCheck,TRUE);
		//DrawCheckMark(&dc,rectCheck,FALSE);
		break;
	case IDM_MENU22:
		DrawMenuItemIcon(&dc,rectCheck,TRUE,IDB_BITMAP1);
		//DrawCheckMark(&dc,rectCheck,TRUE);
		break;
	case IDM_MENU31:
		DrawMenuItemIcon(&dc,rectCheck,TRUE,IDB_BITMAP1);
		break;
	case IDM_MENU13:
		DrawMenuItemIcon(&dc,rectCheck,TRUE,IDB_BITMAP1);
		break;
	}
	dc.Detach();
}



void CNewMenu::ChangeMenuItem(CMenu * pMenu)
{
	int itemCount=pMenu->GetMenuItemCount();//獲取彈出菜單項個數
	for(int i=0;i<itemCount;i++)
	{
		CString *pText=new CString;
		UINT itemID=pMenu->GetMenuItemID(i);//通過菜單項的編號位置獲取菜單項ID號
		pMenu->GetMenuString(i,*pText,MF_BYPOSITION);//獲取菜單文本

		//ModifyMenu函數最後一個參數對應DRAWITEMSTRUCT結構裏的itemData變量
		pMenu->ModifyMenu(i,MF_OWNERDRAW|MF_BYPOSITION|MF_STRING,itemID,(LPSTR)pText);
		if(itemID==-1)//如果是一個彈出式菜單
		{
			ChangeMenuItem(pMenu->GetSubMenu(i));
		}
	}  
}

另外在類中添加兩個繪製菜單項icon和checkMark的函數:

void CNewMenu::DrawMenuItemIcon(CDC * pDC, CRect rect, BOOL bSelected,UINT nIDBmpResource)
{
	//如果不繪製icon,則用背景色填充icon所在區域
	if(!bSelected)
	{
		CBrush MenuItemBKBrush;
		MenuItemBKBrush.CreateSysColorBrush(COLOR_BTNFACE);
		FillRect(pDC->GetSafeHdc(),&rect,(HBRUSH)MenuItemBKBrush);
		return;
	}

	CBitmap bmp;
	if (bmp.LoadBitmap(nIDBmpResource))
	{
		//獲取位圖屬性

		BITMAP bmpInfo;
		bmp.GetBitmap(&bmpInfo);

		// 創建一個與當前DC兼容的內存DC

		CDC dcMemory;
		dcMemory.CreateCompatibleDC(pDC);

		// 將位圖選入內存DC

		CBitmap* pOldBitmap = dcMemory.SelectObject(&bmp);

		//獲取bmp圖片繪製的適當位置
		int nX = rect.left + (rect.Width() - bmpInfo.bmWidth) / 2;
		int nY = rect.top + (rect.Height() - bmpInfo.bmHeight) / 2;

		// 將內存DC中的位圖拷貝到當前DC進行顯示

		pDC->BitBlt(nX ,nY , bmpInfo.bmWidth, bmpInfo.bmHeight, &dcMemory, 
			0, 0, SRCCOPY);

		//dcMemory.SelectObject(pOldBitmap);
		
	}
}


繪製菜單項前面那個√,可以自繪,也可以直接貼一張bmp圖片
void CNewMenu::DrawCheckMark(CDC * pDC, CRect rect, BOOL bSelected)
{
	//這是自繪的方式,但是使用資源圖片直接貼上去會更簡單
	//if(!bSelected)
	//	return;
	//const int nCheckDots = 8;
	//CPoint pt1, pt2, pt3;	//3 point of the checkmark
	//pt1.x = 0;	// 5/18 of the rect width
	//pt1.y = 3;	
	//pt2.x = 2;
	//pt2.y = 5;
	//pt3.x = 7;
	//pt3.y = 0;

	//int xOff = (rect.Width()-nCheckDots)/2 + rect.left ;
	//int yOff = (rect.Height()-nCheckDots)/2 + rect.top;
	//pt1.Offset(xOff, yOff);
	//pt2.Offset(xOff, yOff);
	//pt3.Offset(xOff, yOff);

	//CPen	pen(PS_SOLID, 1, RGB(0, 0, 0));
	//CGdiObject *pOldPen = pDC->SelectObject(&pen);
	//pDC->MoveTo(pt1);
	//pDC->LineTo(pt2);
	//pDC->LineTo(pt3);
	//pt1.Offset(0, 1);
	//pt2.Offset(0, 1);
	//pt3.Offset(0, 1);
	//pDC->MoveTo(pt1);
	//pDC->LineTo(pt2);
	//pDC->LineTo(pt3);
	//pt1.Offset(0, 1);
	//pt2.Offset(0, 1);
	//pt3.Offset(0, 1);
	//pDC->MoveTo(pt1);
	//pDC->LineTo(pt2);
	//pDC->LineTo(pt3);
	//pDC->SelectObject(pOldPen);

	DrawMenuItemIcon(pDC,rect,bSelected,IDB_CHECKMARK);

}

必須讓每個菜單項具有MF_OWNERDRAW屬性,不然接不到WM_MEASUREITEM和WM_DRAWITEM消息,而且菜單項不具有MF_OWNERDRAW屬性, 即使接到消息,也無法自繪,所以上面的ChangeMenuItem函數就是用於修改菜單項屬性
WM_MEASUREITEM和WM_DRAWITEM消息不是直接發給菜單窗口的,會被父窗口給收到,所以得處理父窗口的WM_MEASUREITEM和WM_DRAWITEM消息,給話框類添加這兩個消息處理函數,兩個函數裏的代碼分別如下:

void CFirstDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
 // TODO: Add your message handler code here and/or call default
     if(lpMeasureItemStruct->CtlType==ODT_MENU)//如果類型是菜單
       newMenu.MeasureItem(lpMeasureItemStruct);//調用CNewMenu類的MeasureItem成員函數
       else
    CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
  
}

void CFirstDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
 // TODO: Add your message handler code here and/or call default
 if(lpDrawItemStruct->CtlType==ODT_MENU)
  newMenu.DrawItem(lpDrawItemStruct);
 else
 CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
}

接着我們在對話類添加一個成員變量:

CNewMenu newMenu; (記得要包含頭文件:"NewMenu.h"),然後在對話框類的OnInitDialog函數添加如下代碼:

        newMenu.LoadMenu(IDR_MENU1);
	SetMenu(&newMenu);
	//只更改下主菜單下的第一項,更改全部:newMenu.ChangeMenuItem(&newMenu);
	newMenu.ChangeMenuItem(newMenu.GetSubMenu(1));
	newMenu.CheckMenuItem(IDM_MENU12,MF_CHECKED);
	newMenu.ChangeMenuItem(newMenu.GetSubMenu(2));

現在可以運行了,但是還是存在一些問題,請看下圖:


菜單名的大小並沒有變,這個問題還有待研究

這裏繪製菜單的文字和icon都是在類的DrawItem中完成的,由於剛剛接觸MFC不久,還不知道怎麼寫好點的接口實現動態設置icon,希望得到大家的指點



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章