菜单设计.docx
《菜单设计.docx》由会员分享,可在线阅读,更多相关《菜单设计.docx(22页珍藏版)》请在冰点文库上搜索。
菜单设计
选择资源面板,在Menu(菜单)节点下,双击IDR_MAINFRAME页节点,在编辑区就会将当前应用程序的菜单打开。
如图1-37所示。
该菜单是由MFC应用程序框架所提供的初始菜单。
我们要修改此菜单,添加我们所需要的菜单项。
在修改菜单之前,我们先了解一些菜单编辑的相关知识。
我们都知道,Windows的菜单是一种分级菜单,我们在菜单编辑区中用鼠标选择“文件(F)”菜单项,会打开该菜单项的下级菜单,如图1-38所示。
我们可以称“文件(F)”菜单项为第一级菜单,而其下级菜单为第二级菜单,如果第二级菜单还有下级菜单,选择该第二级菜单,其下级菜单会自动打开,这些菜单就是第三级菜单,以此类推。
选中菜单项后可以按“Delete”键将该菜单项删除,如果该菜单项有下级菜单,这些下级菜单将同时被删除,所以在删除具有下级菜单的菜单项之前,系统会进行讯问是否删除,选择“确定”将会进行删除。
初始菜单中的“文件(F)”和“编辑(E)”两个第一级菜单在我们的应用程序中是不需要的,我们可以将这两个一级菜单删除。
看图1-38,我们注意到在一级菜单项“帮助(H)”右侧有一个虚线矩形框,此处就是用来添加菜单项的,因为它在一级菜单栏上,所以添加的是一级菜单。
同样的,在“文件(F)”菜单项下的二级菜单“退出(X)”下,也有一个虚线矩形框,此处也可以添加菜单项,只是此处添加的是二级菜单。
我们可以用鼠标左键选择虚线矩形框,并按住鼠标左键不放,就可以挪动菜单项的位置。
比如,我们可以将一级菜单的虚线矩形框移到所有一级菜单的最左边,这样我们创建的菜单就会出现在最左边。
如图1-39所示,该菜单已经删除了“文件(F)”和“编辑(E)”两个一级菜单。
其它菜单项的位置也可以如此进行移动,这样我们就可以自由的调整菜单项的显示顺序。
现在我们来添加菜单。
在添加菜单之前,我们需要首先确定我们希望添加什么样的菜单项,菜单的结构是什么样的。
前面所介绍的绘图函数按照所绘制的图形可以大致分为两类,一类绘制的是线形的图形,另一类绘制的是封闭区域图形。
按照此分类,我们将创建下表中所列结构的菜单。
其中MoveTo函数因为本身并不绘制图形,所以单独作为一个二级菜单项,它没有下级菜单。
ID是菜单项的标志符,应用程序框架通过ID来区分各种资源。
一级菜单
二级菜单
三级菜单
ID
绘图函数
线形绘图函数
LineTo
ID_DRAW_LINETO
Polyline
ID_DRAW_POLYLINE
Arc
ID_DRAW_ARC
AngleArc
ID_DRAW_ANGLEARC
PolyBezier
ID_DRAW_POLYBEZIER
区域绘图函数
Rectangle
ID_DRAW_RECTANGLE
RoundRect
ID_DRAW_ROUNDRECT
Ellipse
ID_DRAW_ELLIPSE
Pie
ID_DRAW_PIE
Chord
ID_DRAW_CHORD
Polygon
ID_DRAW_POLYGON
用鼠标双击虚线矩形框,会出现“MenuItemProperties”(菜单项属性)对话框。
该对话框用于设置菜单项属性,如果双击的是一个已经存在的菜单项,则该对话框显示该菜单项的各种属性。
该对话框有两个分页:
“General”(通用属性)和“ExtendedStyles”(扩展类型)。
在“ExtendedStyles”页中只有一个“Right-to-leftorderandalignment”复选框,选择该复选框,菜单项标题将由右到左显示,并向右对齐。
通常情况我们都不会选择它。
在“General”页中可以设置菜单的各种属性。
“Caption”输入框。
用于输入菜单标题,应用程序执行时,该标题是我们所看到的菜单项的文字。
“Pop-up”复选框。
选中此复选框表示此菜单项具有下级菜单,此时“ID”下拉输入框,“Prompt”(说明)输入框及“Separator”复选框将处于灰色无效状态。
“ID”下拉输入框。
不选择“Pop-up”复选框,“ID”下拉输入框可以输入,在此输入此菜单项的ID。
下拉列表中会列出当前应用程序中所有已经使用的ID。
同时,如果此ID可以输入,则必须输入,否则系统将自动生成一个ID。
“Separator”复选框。
选中该复选框,菜单项将变成一条菜单分隔线,此时除“Caption”外,其它属性将都不能进行设置,而“Caption”虽然可以输入,但是一旦在“Caption”中输入值,则“Separator”复选框的选择将自动取消。
“Checked”复选框。
选中该复选框,应用程序运行时将在该菜单项前预先设置一个检查符号。
“Inactive”复选框。
选中该复选框,在应用程序运行时该菜单项不出现。
“Grayed”复选框。
选中该复选框,该菜单项以灰色显示,表示此菜单项不可用。
“Help”复选框。
选中该复选框,应用程序运行时将自动检查菜单项的合法性。
“Prompt”输入框。
用于输入菜单项的说明,当应用程序运行时,如果鼠标悬停在菜单项上,该说明将在状态栏中显示。
“Break”下拉框。
在此下拉框中有三项可选:
“None”,“Column”,“Bar”。
它们决定菜单的显示样式,默认是“None”。
选择“Column”,菜单项将以列的方式进行排列;选择“Bar”,菜单项以列的方式进行排列的同时将由竖线分隔开。
读者可以自行设置来观察效果,本应用程序的菜单都采用默认的“None”。
同时要注意的是,一个菜单项选择了非默认的菜单样式,所有的同级菜单项都将受到影响,如果同级菜单项中既有选择“Column”的,也有选择“Bar”的,则菜单样式将为“Bar”所指定的样式。
我们在当前打开的菜单项属性对话框中选择“Pop-up”复选框,并在“Caption”输入框输入“绘图函数”,关闭对话框,我们将看到“绘图函数”一级菜单已经创建。
同时在该菜单项下有一个虚线矩形框,我们可以双击它来输入下级菜单。
如图1-41所示。
双击图1-41中“绘图函数”菜单项下的虚线矩形框,在打开的菜单项属性对话框中,选择“Pop-up”复选框,并在“Caption”输入框输入“线形绘图函数”,关闭对话框,这样就创建了“线形绘图函数”二级菜单。
如图1-42所示。
双击图1-42中“线形绘图函数”菜单项右侧的虚线矩形框,在打开的菜单项属性对话框中,不选择“Pop-up”复选框,在“Caption”输入框输入“LineTo”,在“ID”下拉输入框中输入“ID_DRAW_LINETO”,然后关闭对话框,这样就创建了“LineTo”三级菜单。
如图1-43所示。
按照上述方法,可以将我们所需要的菜单项创建出来,二级菜单项输入“Caption”,并且选择“Pop-up”复选框,三级菜单项不选择“Pop-up”复选框,输入“Caption”和“ID”,具体值在菜单结构表中已经列出。
创建完的菜单如图1-44所示。
我们可以在“线形绘图函数”菜单项和“区域绘图函数”菜单项之间加上一条分隔线。
双击“区域绘图函数”菜单项下的虚线矩形框,在打开的菜单项属性对话框中选择“Separator”复选框。
然后关闭对话框,并将该菜单项移动到“区域绘图函数”菜单项上面。
其结果如图1-45所示。
此时我们运行应用程序,可以看到应用程序菜单已经是我们所创建的了。
但是各菜单项都处于灰色不可用状态,这是因为我们还没有为各菜单项连接处理函数。
下面我们将用ClassWizard(类向导)为菜单项来连接处理函数。
1.1.1使用ClassWizard为菜单项连接处理函数
在VisualStudioC++6.0开发环境中,选中“View”菜单下的“ClassWizard”菜单项,或者按“Ctrl+W”的快捷键组合,将出现“MFCClassWizard”(MFC类向导)对话框。
如图1-46所示。
MFC类向导的功能很多,在进行MFC编程的时候经常会用到。
我们现在只介绍MFC类向导中我们将用到的功能,其它功能等到后面用到的时候再详细介绍。
MFC类向导对话框有“MessageMaps”等五个分页,其中“MessageMaps”分页是我们最常用到的。
为菜单项连接处理函数也是在这一分页中。
在MFC类向导对话框的“MessageMaps”分页中,“Project:
”下拉框中是当前正在编辑的项目名称,图1-46中显示的就是我们现在所编辑的项目的名称DrawTest;“Classname:
”下拉框中列出了当前项目中所包含的类,图1-46中当前正在显示的是CMainFrame类,我们可以通过下拉列表来选择其它类;“ObjectIDs:
”列表框中显示的是当前选择的类以及应用程序中所有的资源ID;“Messages:
”列表框中显示的是“ObjectIDs:
”列表框中所选中的类或者ID所代表的资源所支持的MFC系统消息,在图1-46中“ObjectIDs:
”列表框中选中了CMainFrame类,表示“Messages:
”列表框中显示的CMainFrame类所支持的MFC系统消息;“Memberfunctions:
”列表框中显示的是选中的类当前已有的成员函数。
我们在“Classname:
”下拉框中选择CDrawTestView类,然后在“ObjectIDs:
”列表框中选中ID_DRAW_LINETO,这是为我们刚才创建的菜单中的“LineTo”三级菜单所设置的ID。
在“ObjectIDs:
”列表框中我们可以看到所设置的所有ID。
选中ID_DRAW_LINETO后,我们会看到在“Messages:
”列表框中列出了它所支持的MFC系统消息,也就是菜单项所支持的MFC系统消息。
而在“Memberfunctions:
”列表框中列出了CDrawTestView类当前所有的成员函数。
如图1-47所示。
从图1-47中可以看到,菜单项支持两种消息:
COMMAND消息和UPDATE_COMMAND_UI消息。
COMMAND消息是鼠标单击菜单项时应用程序发出的消息,UPDATE_COMMAND_UI消息是菜单项形成或者发生改变时应用程序发出的消息。
我们是要为菜单项连接用户点击时的处理函数,所以选择COMMAND消息。
此时,右侧的“AddFunction”按钮变为可用,我们可以点击此按钮,或者双击COMMAND消息,都可以打开“AddMemberFunction”(添加成员函数)对话框,如图1-48所示。
在对话框的“Memberfunctionname:
”输入框中,显示的是MFC类向导为新的成员函数所自动生成的一个函数名,我们可以修改函数名,但是建议还是采用生成的函数名称。
在对话框下端,列出了这个处理函数是针对ID为ID_DRAW_LINETO资源的COMMAND消息所创建的处理函数。
点击OK按钮,则此成员函数就被加入到CDrawTestView类中。
如图1-49所示。
前面选择CDrawTestView类,就是为了将菜单项的处理函数加入到该类中,当然这个处理函数也可以加入到其它类中,但是CDrawTestView类是视图类,用于进行图形绘制的处理函数最好还是放在该类中。
在“Memberfunctions:
”列表框中我们可以看到刚才添加的成员函数,并且处于选中状态,我们可以双击该成员函数,或者点击右侧“EditCode”按钮,系统会自动定位到OnDrawLineto函数处,让用户进行代码编写。
当应用程序运行时,如果用户点击“LineTo”菜单项,则应用程序框架会自动调用OnDrawLineto函数。
我们编写OnDrawLineto函数,用它来演示LineTo绘图函数,输入如下代码:
voidCDrawTestView:
:
OnDrawLineto()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.MoveTo(300,300);
dc.LineTo(400,400);
}
按照上面的方法,添加其它菜单项的处理函数,我们可以在MFC类向导中一次把所有要添加的处理函数都添加完,再对其进行编辑。
编写“Polyline”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawPolyline()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
POINTp[5];
p[0].x=300;p[0].y=200;
p[1].x=400;p[1].y=200;
p[2].x=400;p[2].y=300;
p[3].x=350;p[3].y=350;
p[4].x=320;p[4].y=220;
dc.Polyline(p,5);
}
编写“Arc”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawArc()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.Arc(100,200,300,300,280,120,120,280);
}
编写“AngleArc”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawAnglearc()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.MoveTo(450,100);
dc.AngleArc(550,200,50,90,270);
}
编写“PolyBezier”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawPolybezier()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
POINTp[7];
p[0].x=200;p[0].y=100;
p[1].x=200;p[1].y=200;
p[2].x=300;p[2].y=200;
p[3].x=400;p[3].y=300;
p[4].x=500;p[4].y=400;
p[5].x=300;p[5].y=400;
p[6].x=100;p[6].y=450;
dc.PolyBezier(p,7);
}
编写“Rectangle”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawRectangle()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.Rectangle(450,100,650,250);
}
编写“RoundRect”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawRoundrect()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.RoundRect(650,250,850,400,20,20);
}
编写“Ellipse”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawEllipse()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.Ellipse(450,400,650,500);
}
编写“Pie”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawPie()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.Pie(100,300,300,400,280,220,120,380);
}
编写“Chord”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawChord()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
dc.Chord(100,400,300,500,280,320,120,480);
}
编写“Polygon”菜单项处理函数,输入如下代码:
voidCDrawTestView:
:
OnDrawPolygon()
{
//TODO:
Addyourcommandhandlercodehere
CClientDCdc(this);
POINTp[4];
p[0].x=300;p[0].y=150;
p[1].x=350;p[1].y=1;
p[2].x=420;p[2].y=200;
p[3].x=350;p[3].y=150;
dc.Polygon(p,4);
}
那么应用程序是如何将菜单项和处理函数实际连接起来的呢?
我们可以看一下CDrawTestView类的类文件的类声明之前有一个BEGIN_MESSAGE_MAP宏,在这个宏中,应用程序完成了资源ID与其处理函数的实际连接,现在该宏内容如下:
BEGIN_MESSAGE_MAP(CDrawTestView,CView)
//{{AFX_MSG_MAP(CDrawTestView)
ON_COMMAND(ID_DRAW_LINETO,OnDrawLineto)
ON_COMMAND(ID_DRAW_POLYLINE,OnDrawPolyline)
ON_COMMAND(ID_DRAW_ARC,OnDrawArc)
ON_COMMAND(ID_DRAW_ANGLEARC,OnDrawAnglearc)
ON_COMMAND(ID_DRAW_POLYBEZIER,OnDrawPolybezier)
ON_COMMAND(ID_DRAW_RECTANGLE,OnDrawRectangle)
ON_COMMAND(ID_DRAW_ROUNDRECT,OnDrawRoundrect)
ON_COMMAND(ID_DRAW_ELLIPSE,OnDrawEllipse)
ON_COMMAND(ID_DRAW_PIE,OnDrawPie)
ON_COMMAND(ID_DRAW_CHORD,OnDrawChord)
ON_COMMAND(ID_DRAW_POLYGON,OnDrawPolygon)
//}}AFX_MSG_MAP
//Standardprintingcommands
ON_COMMAND(ID_FILE_PRINT,CView:
:
OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,CView:
:
OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView:
:
OnFilePrintPreview)
END_MESSAGE_MAP()
可见,正是在这个宏中完成了菜单项和处理函数的实际连接。
这部分代码是由系统自动添加的,用户并不需要自己编写,用户只需要知道实际连接在代码上是如何实现的就可以了。
同时,我们可以看一下这些处理函数在头文件中的声明。
打开CDrawTestView类的头文件,可以看到处理函数的声明如下:
protected:
//{{AFX_MSG(CDrawTestView)
afx_msgvoidOnDrawLineto();
afx_msgvoidOnDrawPolyline();
afx_msgvoidOnDrawArc();
afx_msgvoidOnDrawAnglearc();
afx_msgvoidOnDrawPolybezier();
afx_msgvoidOnDrawRectangle();
afx_msgvoidOnDrawRoundrect();
afx_msgvoidOnDrawEllipse();
afx_msgvoidOnDrawPie();
afx_msgvoidOnDrawChord();
afx_msgvoidOnDrawPolygon();
//}}AFX_MSG
同样,这些声明也是由系统自动添加的,不需要用户自己编写。
运行应用程序,我们可以通过选择菜单项来看对应的绘图函数的执行结果。
此时,如果把应用程序窗口最小化,然后再恢复该窗口,我们就会看到原来在视图区所绘制的图形已经没有了,这是因为我们现在所编写的绘图代码是OnDraw函数所调用不到的。
因此在窗口重画的时候,并不能把原来已有的图形重新绘制出来。
为了能使绘制的图形在视图重画的时候能够正确显示,就需要将图形的相关数据先存储起来,然后在OnDraw函数中将这些图形重新绘制一遍。
具体的做法我们将在下一章中详细介绍。
1.1.2主动视图重画
前面我们介绍了OnDraw函数是在应用程序窗口需要重新绘制的时候,由应用程序框架来自动调用的。
有时候,我们需要通过程序代码来引起视图重画,即引起OnDraw函数的调用。
MFC提供了相应的函数,使我们可以主动引发视图重画。
主动视图重画可以分为两种类型:
全部重画和局部重画。
1
1.1
1.2
1.3
1.4
1.5
1.5.1
1.5.2
1.5.3
1.5.3.1全部重画
全部重画可以通过调用窗口类的Invalidate函数来实现,其函数声明如下:
voidCWnd:
:
Invalidate(BOOLbErase=TRUE);
参数bErase为TRUE时,应用程序在调用OnDraw函数之前会先清空视图区;如果为FALSE,则不清空。
Invalidate函数在调用的时候可以不传入参数,此时默认bErase为TRUE。
这里需要注意的是,调用Invalidate函数后,应用程序框架并不是马上调用OnDraw函数进行重画,而是等到调用Invalidate函数的函数执行完毕之后,才会调用OnDraw函数,所以调用Invalidate函数通常写在函数执行的最后一句。
1.5.3.2局部重画
全部重画是针对整个视图区进行重画,而有时候,绘图工作只是在视图区中的一个局部区域进行的,此时进行全部重画,会造成一些不必要的资源浪费,有时还会使屏幕产生闪烁的现象。
针对这种问题,可以采用局部重画的方法——使指定的区域无效,而不是全部视图区都失效。
局部重画通过调用InvalidateRect函数或者InvalidateRgn函数来实现。
◆InvalidateRect的函数声明如下:
voidCWnd:
:
InvalidateRect(LPCRECTlpRect,BOOLbErase=TRUE);
其中参数lpRect指定了一个矩形区域,如果该参数传入NULL的话,代表的是整个视图区。
参数bErase的含义与Invalidate函数中的相同,只是此处清空