这两天在菜单栏,需要改变菜单栏字体的大小,但是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;iGetMenuItemID(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));
}
}
}
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);
}
}
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);
}
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);
}
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,希望得到大家的指点