菜单工具栏状态栏.docx
《菜单工具栏状态栏.docx》由会员分享,可在线阅读,更多相关《菜单工具栏状态栏.docx(12页珍藏版)》请在冰豆网上搜索。
![菜单工具栏状态栏.docx](https://file1.bdocx.com/fileroot1/2023-1/21/3fad8819-adcf-4c84-982e-99ba9a05858d/3fad8819-adcf-4c84-982e-99ba9a05858d1.gif)
菜单工具栏状态栏
菜单、工具栏和状态栏
一、菜单
1.新建一个单文档SDI工程1MiniCAD,在原菜单右侧添加“绘图”子菜单:
画线(&L)\tCtrl+L画一条直线\n画线
菜单项名热键加速键(快捷键)状态栏提示工具栏图标提示
实现上述菜单功能。
2.概念:
菜单栏、菜单、子菜单、菜单项、分隔条(Separator)
菜单栏从左到右是从0开始的索引,菜单项由上至下也是从0开始的索引,访问子菜单和菜单项均通过其索引或标识ID进行。
在计算菜单项的索引的时候,分隔栏Separator也要计算在内。
相关重要函数:
CMenu*GetMenu();//得到窗口菜单栏对象指针。
CMenu*GetSubMenu(4);/获得指向弹出菜单对象指针
UINTCheckMenuItem();//在菜单项前面打勾
BOOLSetDefaultItem();//使菜单项变粗变黑
UINTEnableMenuItem();//使菜单项有效,无效,或变灰。
BOOLSetMenuItemBitmaps();//设置位图菜单。
BOOLSetMenu(CMenu*pMenu);//在当前窗口上设置新菜单或移除菜单。
HMENUDetach();//解除捆绑
示例:
a.在View类中,使菜单项“普通模式”成为打勾状态:
GetParent()->GetMenu()->GetSubMenu(4)->
CheckMenuItem(ID_DRAW_NONE,MF_BYCOMMAND|MF_CHECKED);
b.在CMainFrame:
:
OnCreate()中,使菜单项“普通模式”成为默认菜单:
GetMenu()->GetSubMenu(4)->SetDefaultItem(ID_DRAW_NONE);
c.在CMainFrame:
:
OnCreate()中,使菜单项“新建”变成灰色,不可用状态:
//禁止“打印预览”菜单项:
//EnableMenuItem()需要将CFrameWnd:
:
m_bAutomenuEnable设置为FALSE,
//如果该数据成员为TRUE(缺省值),工作框将自动地禁止没有
//ON_COMMAND或者ON_UPDATE_COMMAND_UI的菜单项。
m_bAutoMenuEnable=FALSE;
GetMenu()->GetSubMenu(0)->EnableMenuItem(6,
MF_BYPOSITION|MF_DISABLED|MF_GRAYED);
d.在CMainFrame:
:
OnCreate()中,给SaveAs菜单项增加图标:
CStringstr;
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);
CBitmapbitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(3,MF_BYPOSITION,&bitmap,&bitmap);
bitmap.Detach();//使HBITMAP和CBitmap解除捆绑,否则会使SetMenuItemBitmaps失效。
说明:
intGetSystemMetrics()获取系统信息度量。
可以用它来获取菜单标题的尺寸从而设置位图标题菜单中位图的大小。
e.在CMainFrame:
:
OnCreate()中,移除/增加菜单
CMenu*pMenu=this->GetMenu();
this->SetMenu(NULL);//移除菜单
this->SetMenu(pMenu);//增加菜单
3.在资源管理器中设置加速键。
然后在CApp类的InitInstance()中加载快捷键,使定义的Ctrl+L等起作用:
HACCELhAccelTable=
LoadAccelerators(theApp.m_hInstance,MAKEINTRESOURCE(IDR_MAINFRAME));
4.菜单消息传递顺序:
View类--Doc类--CMainFrame类--App类。
菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。
5.使用TrackPopupMenu函数,添加右键弹出菜单:
1)用资源管理器添加一个菜单资源,也可以利用主菜单中的某个子菜单作为要弹出的菜单。
2)在鼠标右键消息响应函数中,加载菜单资源,并获得要显示的子菜单指针,并用该指针调用TrackPopupMenu函数便完成任务(但要注意:
鼠标响应函数传进来的坐标是客户区坐标,而TrackPopupMenu函数中使用的是屏幕坐标,在调用TrackPopupMenu前要调用ClientToScreen客户区坐标到屏幕坐标的转换),如:
voidCMiniCADView:
:
OnRButtonUp(UINTnFlags,CPointpoint)
{
//CMenumenu;
//menu.LoadMenu(IDR_MAINFRAME);
//CMenu*pPopup=menu.GetSubMenu(4);
//ClientToScreen(&point);
//pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,GetParent());
CView:
:
OnRButtonUp(nFlags,point);
}
3)可用WM_CONTEXTMENU消息替换在鼠标右键消息响应函数来完成TrackPopupMenu的工作。
如:
voidCMiniCADView:
:
OnContextMenu(CWnd*pWnd,CPointpoint)
{
//CMenumenu;
//menu.LoadMenu(IDR_MAINFRAME);
//CMenu*pPopup=menu.GetSubMenu(4);
//pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,GetParent());
GetParent()->GetMenu()->GetSubMenu(4)->TrackPopupMenu(
TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,GetParent());
}
6.动态菜单编程:
●创建动态菜单。
常用函数有:
CMenu:
:
CreatePopupMenu//创建一个Popup菜单,如“电话本”
CMenu:
:
DrawMenuBar//绘制菜单
CMenu:
:
AppendMenu//给菜单增加一项,查MSDN,注意nFlags参数的用法
如:
GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"电话本");
m_menu.AppendMenu(MF_STRING,m_nIndex+IDM_PHONE1,"张三");
●为动态菜单添加响应函数:
(注意,不是自定义消息)
首先在resource.h中定义菜单ID,如:
#defineIDM_PHONE132777
#defineIDM_PHONE232778
第二,消息映射,如:
ON_COMMAND(IDM_PHONE1,OnPhone1)
ON_COMMAND(IDM_PHONE2,OnPhone2)
第三,定义菜单响应函数,如:
afx_msgvoidOnPhone1();
afx_msgvoidOnPhone2();
第四,实现菜单响应函数。
示例:
voidCMiniCADView:
:
OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
if(nChar==13)//回车
{
m_nIndex++;//m_nIndex初值为-1
if(0==m_nIndex)//说明“电话本”菜单项还没有创建,则创建之
{
m_menu.CreatePopupMenu();
this->GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"电话本");
GetParent()->DrawMenuBar();
}
m_menu.AppendMenu(MF_STRING,m_nIndex+IDM_PHONE1,m_strLine.Left(m_strLine.Find('')));
//将记录保存进字符串数组
m_strArray.Add(m_strLine);//CStringArray
//清空,清屏,为输入下一个人的号码做好准备
m_strLine="";
this->Invalidate();//清屏
}
else
{
CClientDCdc(this);
m_strLine+=(char)nChar;
dc.TextOut(10,10,m_strLine);
}
CView:
:
OnChar(nChar,nRepCnt,nFlags);
}
voidCMiniCADView:
:
OnPhone1()
{
CClientDCdc(this);
dc.TextOut(10,10,m_strArray[0]);
}
voidCMiniCADView:
:
OnPhone2()
{
CClientDCdc(this);
dc.TextOut(10,10,m_strArray[1]);
}
二、工具条
编辑工具栏资源:
在资源管理视图中,打开ToolBar目录。
新建一个工具条资源IDR_TOOLBAR1。
如下图:
工具条一般是和菜单相关联的,将工具栏上的某个图标的ID设置为菜单项的ID即可。
工具栏图标的索引记数顺序也是从左到右,由0开始,分隔符也算索引号。
CToolBar属于控制栏中的一种,由CControlBar派生。
一般在CMainFrame:
:
OnCreate函数中创建工具栏,分2个步骤:
先用CToolBar:
:
Create建立工具条对象,建立工具条窗口句柄。
然后CToolBar:
:
LoadToolBar加载工具栏资源
技巧:
要增加一个新的m_wndToolBar1,可以在CMainFrame:
:
OnCreate中,复制m_wndToolBar的相关代码,将m_wndToolBar改成m_wndToolBar1,将资源IDR_MAINFRAME改成IDR_TOOLBAR1即可。
主要函数:
●CreateEx函数:
如果dwCtrlStyle=0,则工具栏为VC5以前的凸起风格。
dwStyle包含了丰富的工具栏风格
CBRS_SIZE_DYNAMIC和CBRS_SIZE_FIXED是两个相反的风格。
CBRS_SIZE_DYNAMIC是动态尺寸的,在漂浮时可拖拉改变尺寸。
CBRS_SIZE_FIXED在漂浮时是紧凑的,无法拖拉改变尺寸。
CBRS_GRIPPER风格包含一个突起的掐子,使嵌入的工具栏更方便的拖。
CBRS_TOOLTIPS风格使鼠标在工具按钮上方时,可以现实黄色小提示。
●DockControlBar停靠
●FloatControlBar浮动
●ShowControlBar显示
三、再回首菜单栏,往事不再依旧:
UPDATE_COMMAND_UI消息改变了这一切。
增加菜单:
查看-->绘图栏,添加消息ON_COMMAND(ID_VIEW_DRAWBAR,OnViewDrawbar):
voidCMainFrame:
:
OnViewDrawbar()
{
ShowControlBar(&m_wndToolBar1,!
m_wndToolBar1.IsWindowVisible(),FALSE);
}
继续添加消息ON_UPDATE_COMMAND_UI(ID_VIEW_DRAWBAR,OnUpdateViewDrawbar):
voidCMainFrame:
:
OnUpdateViewDrawbar(CCmdUI*pCmdUI)
{
pCmdUI->SetCheck(m_wndToolBar1.IsWindowVisible());
}
菜单项状态的维护是基于UPDATE_COMMAND_UI消息,谁捕获该消息,MFC就为谁创建一个CCmdUI对象。
可以手工或用ClassWizard来给菜单项添加UPDATE_COMMAND_UI消息响应。
在OnUpdatexxx中用CCmdUI控制菜单项可用、不可用,变灰,打勾等操作。
只能设置,而不能获取。
为菜单ID_DRAW_NONE,ID_DRAW_LINE,ID_DRAW_RECT,ID_DRAW_ELLIPSE添加UPDATE_COMMAND_UI消息,写入SetCheck代码,同时去掉ON_COMMAND中起打勾作用的代码,再次运行,会发现,绘图栏上对应的按钮处于被按下状态了。
对用户来说,就更加直观了。
修改后的部分代码如下:
voidCMiniCADView:
:
OnDrawNone()
{
m_nDrawType=0;
}
voidCMiniCADView:
:
OnDrawLine()
{
m_nDrawType=1;
}
voidCMiniCADView:
:
OnUpdateDrawNone(CCmdUI*pCmdUI)
{
pCmdUI->SetCheck(0==m_nDrawType);
}
voidCMiniCADView:
:
OnUpdateDrawLine(CCmdUI*pCmdUI)
{
pCmdUI->SetCheck(1==m_nDrawType);
}
绘图栏上的按钮被按下的效果如下图所示:
四、状态栏
上面的工具栏可以有多个,状态栏一般只有一个。
在CStatusBar中,常用的状态栏操作函数有:
//Create:
创建CStatusBar
SetIndicators:
设置状态条上指示器
CommandToIndex:
获取Index
SetPaneText:
设置指示器文字
SetPaneStyle:
设置指示器风格
SetPaneInfo:
设置指示器属性
如下代码在状态栏中增加了一个显示当前时间的指示器:
a.在String资源中添加IDS_TIMER23:
59:
59的定义,不要ID_INDICATOR_TIMER,否则不显示时间
b.在MainFrm.cpp中的indicators数组中添加IDS_TIMER
c.在MainFrm.cpp中OnCreate()中添加
//设置时间栏
CTimet=CTime:
:
GetCurrentTime();
CStringstr=t.Format("%H:
%M:
%S");
intindex=m_wndStatusBar.CommandToIndex(IDS_TIMER);
m_wndStatusBar.SetPaneText(index,str);
this->SetTimer(123,1000,NULL);//每秒更新一次时间栏
d.在OnTimer()中添加上述更新时间的代码
五、完善MiniCAD,使其具有图形保持功能。
上面的MiniCAD绘图后,如果改变一下窗口大小,就会擦掉所绘的图形。
这是因为我们的绘图代码没有写在OnDraw函数中,而当窗口重绘(WM_PAINT)时,只会重绘OnDraw中代码所写的绘图功能;但是上述工程在OnDraw中没有写任何绘图代码,因此,WM_PAINT时就擦掉了在OnLButtonUp等事件中绘制的图形。
解决思路:
将用户绘制的一个个图形都保存起来,然后在OnDraw中一个个的重绘出来。
1、定义一个CShape类,用于表示用户所画图形的数据结构,包括图形类型、起点、终点和颜色。
classCShape
{
public:
UINTm_nDrawType;//绘图类型0不绘图1直线2矩形3椭圆
CPointm_ptBegin;//起点
CPointm_ptEnd;//终点
COLORREFm_color;//颜色
CShape();
CShape(UINTnDrawType,CPointptBegin,CPointptEnd,COLORREFm_color)
{
this->m_nDrawType=nDrawType;
this->m_ptBegin=ptBegin;
this->m_ptEnd=ptEnd;
this->m_color=color;
};
virtual~CShape();
};
2、定义一个CMiniCADView成员变量CPtrArraym_ptrArray,用于保存所绘制的一个个图形。
在OnLButtonUp最后面,输入以下代码:
voidCMiniCADView:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
//......
//保存当前图形
//CShapeshape(m_nDrawType,m_ptBegin,m_ptEnd);
//m_ptrArray.Add(&shape);//这种方式保存不了真正的数据,因为shape析构时就销毁了
CShape*pShape=newCShape(m_nDrawType,m_ptBegin,m_ptEnd,m_color);
m_ptrArray.Add(pShape);
CView:
:
OnLButtonUp(nFlags,point);
}
3、在OnDraw中进行绘图:
#include"Shape.h"
voidCMiniCADView:
:
OnDraw(CDC*pDC)
{
CMiniCADDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
//保存老的画笔和画刷,用于后面的恢复。
CPen*pOldPen=pDC->GetCurrentPen();
CBrush*pOldBrush=pDC->GetCurrentBrush();
//透明画刷
CBrush*pBrush=CBrush:
:
FromHandle((HBRUSH):
:
GetStockObject(NULL_BRUSH));
pDC->SelectObject(pBrush);
for(inti=m_ptrArray.GetSize()-1;i>=0;i--)
{
CShape*pShape=(CShape*)m_ptrArray.GetAt(i);
//设置画笔颜色:
m_color色
CPenpen(PS_SOLID,1,pShape->m_color);
pDC->SelectObject(&pen);
//开始绘图
if(pShape->m_nDrawType==1)//画线
{
pDC->MoveTo(pShape->m_ptBegin);
pDC->LineTo(pShape->m_ptEnd);
}
elseif(pShape->m_nDrawType==2)//画矩形
{
pDC->Rectangle(pShape->m_ptBegin.x,pShape->m_ptBegin.y,
pShape->m_ptEnd.x,pShape->m_ptEnd.y);
}
elseif(pShape->m_nDrawType==3)//画椭圆
{
pDC->Ellipse(pShape->m_ptBegin.x,pShape->m_ptBegin.y,
pShape->m_ptEnd.x,pShape->m_ptEnd.y);
}
}
//恢复
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
}
4、其他:
AfxGet...SetCursor
voidCMiniCADView:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
//设置鼠标位置状态栏
//#include"MainFrm.h"
//public:
CToolBarm_wndStatusBar
CMainFrame*pMainFrame=(CMainFrame*)AfxGetMainWnd();
CStringsPos;
sPos.Format("%d,%d",point.x,point.y);
intindex=pMainFrame->m_wndStatusBar.CommandToIndex(IDS_MOUSEPOS);
pMainFrame->m_wndStatusBar.SetPaneText(index,sPos);
//设置绘图时的光标
//:
:
SetClassLong(m_hWnd,GCL_HCURSOR,(LONG):
:
LoadCursor(NULL,m_nDrawType==0?
IDC_ARROW:
IDC_CROSS));
SetCursor(:
:
LoadCursor(NULL,m_nDrawType==0?
IDC_ARROW:
IDC_CROSS));
}
作业:
在状态栏添加一个鼠标位置指示器,以显示鼠标当前的位置。