[MFC]Shapes程序(4):菜单项的加速键、MFC注释宏

来源:互联网 发布:微信公众号推荐 知乎 编辑:程序博客网 时间:2024/05/18 10:58

1. 加速键的作用:之前已经讲过了,就是比快捷键还快的按法,一般快捷键需要Alt+顶层菜单项的字母键+子菜单中菜单项的字母键,如果子菜单还有层叠子菜单,则还要再加一个层叠子菜单中菜单项的字母键,而加速键MFC规定,最多只能定义两个键组合,比如Ctrl+N之类的;


2. 编写加速键资源:

    1) 加速键也是一种资源,也需要用.rc脚本来定义,加载方式和菜单资源差不多;

    2) 定义菜单需要关键字MENU以及菜单资源名,定义加速键同样也需要资源名,当然资源名可以任取,而关键字则成为了ACCELERATORS;

    3) 先看下面一个典型的加速键资源的定义:

IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLEBEGIN"N",ID_FILE_NEW,VIRTKEY, CONTROL"O",ID_FILE_OPEN,VIRTKEY, CONTROL"S",ID_FILE_SAVE,VIRTKEY, CONTROL"Z",ID_EDIT_UNDO,VIRTKEY, CONTROLVK_BACK,ID_EDIT_UNDO,VIRTKEY, ALT"X",ID_EDIT_CUT,VIRTKEY, CONTROLVK_DELETE,ID_EDIT_CUT,VIRTKEY, SHIFT"C",ID_EDIT_COPY,VIRTKEY, CONTROLVK_INSERT,ID_EDIT_COPY,VIRTKEY, CONTROL"V",ID_EDIT_COPY,VIRTKEY, CONTROLVK_INSERT,ID_EDIT_COPY,VIRTKEY, SHIFTEND
         i. 资源属性PRELOAD和MOVEABLE都是16位的产物,32位环境下不起作用;

         ii. BEGIN-END中的每一行都定义了一个加速键以及加速键对应的菜单命令;

         iii. 每行第一项定义的是加速键的虚拟键代码,注意!对于控制键,例如DELETE、HOME、INSERT等都有相应的虚拟键代码宏,即上面出现的以VK_作为前缀的宏,即Virtual Key的缩写,但是对于正常的英文字符以及阿拉伯数字在任何系统中都没有定义过虚拟键代码宏,只能通过字符串(及双引号"")的形式表示;

!虚拟键代码对应的是键盘上的每个按键,注意和ASCII码区别,ASCII码里字母有大小写之分,但是在键盘上就只有26个英文字母键而不是26个小写字母键和大写字母键,虚拟键码是键盘直接产生的,是一种硬编码,而ASCII码是这种硬编码进入操作系统后转化而成的,因此在.rc文件中就暂且先用ASCII码来代替虚拟键代码宏,而第三项的VIRTKEY关键字则提示编译器,第一项是虚拟键代码,编译器会将ASCII字符替换成真正的虚拟键代码;

!Rc.exe的编译规则:首先会先进行预处理,和C编译器一样的,先将所有的宏替换成相应的数字或字符串,这里就先将VK_等宏替换成原本的数值,同时把所以出现的字符或字符串替换成相应的ASCII码数字,比如这里,扫描到"N"、"O"等,就会在相应的位置替换成ASCII码数字78、79,接下来再扫描到VIRTKEY关键字,就需要将前面的ASCII码转换成对应键的虚拟键盘代码数字,最后再是编译资源生成.icon、.rc2等真正的二进制资源文件了;

         iv. 第二项指示和加速键关联的菜单ID,当加速键按下后就会产生相应的WM_COMMAND消息了;

         v. 最后一下定义了一个额外的控制键(当然可以没有,但是不符合Windows用户的使用习惯),只能充CONTROL、ALT和SHIFT三者中选;


!!!关于控制键:其实可以有多个控制键,即还能定义一个控制键,比如Alt+Shift+U等等三键加速键;

!!!关于第三项:其实还有一种选择,那就是ASCII,即加速键直接用ASCII码定义,但不过这只能用于字母键和数字键,其中字母键的第一项必须用小写,作用和VIRTKEY的大写字母第一项一样,一般为了统一还是都用VIRTKEY,因为非字母和数字键的控制键只有VIRTKEY而没有ASCII;

!!!还可以添加其它属性,比如最常用的是NOINVERT,该项添加到一样的最末尾,这项的作用是当你按下加速键时,加速键对应的菜单项所对应的顶层菜单项会闪烁,这个效果可能在Win7下要用更高级的VS2010等才会有效果,但是该效果在MAC OS X中非常地明显,VC6.0输出的程序有无该关键字效果貌似都一样,都是没有闪烁。。。


Shapes程序中的菜单资源:

IDR_MAINFRAME MENU PRELOAD DISCARDABLE BEGINPOPUP "&File"BEGINMENUITEM "E&xit\tAlt+X",                       ID_APP_EXITENDPOPUP "&Shape"BEGINMENUITEM "&Circle\tF9",ID_SHAPE_CIRCLEMENUITEM "&Triangle\tF10",ID_SHAPE_TRIANGLEMENUITEM "&Square\tF11",ID_SHAPE_SQUAREENDEND


3. 加速键资源的加载和挂接:

    1) 和菜单一样都是资源,所以加速键资源同样需要加载和挂接;

    2) 使用Frame的LoadAccelTable加载的同时挂接:BOOL CFrameWnd::LoadAccelTable(LPCTSTR lpszResourceName);,如果成功范围TRUE,否则为FALSE;

    3) 也可以使用LoadFrame加载并挂接,只要将菜单资源ID和加速键资源ID共享同一个名称就可以同时加载了;

Shapes程序中的加速键资源加载和挂接:

BOOL CShapesApp::InitInstance(){// Standard initialization// If you are not using these features and wish to reduce the size//  of your final executable, you should remove from the following//  the specific initialization routines you do not need.// Change the registry key under which our settings are stored.// TODO: You should modify this string to be something appropriate// such as the name of your company or organization.SetRegistryKey(_T("Local AppWizard-Generated Applications"));// To create the main window, this code creates a new frame window// object and then sets it as the application's main window object.CMainFrame* pFrame = new CMainFrame; // 创建窗口框架m_pMainWnd = pFrame;// create and load the frame with its resources// 在一般情况下尽量少用MFC预定义的东西做事情,因此这里用自己定义的pFrame来调用相关函数pFrame->LoadFrame(IDR_MAINFRAME,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,NULL);// The one and only window has been initialized, so show and update it.pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;}
加速键资源脚本:

BOOL CShapesApp::InitInstance(){// Standard initialization// If you are not using these features and wish to reduce the size//  of your final executable, you should remove from the following//  the specific initialization routines you do not need.// Change the registry key under which our settings are stored.// TODO: You should modify this string to be something appropriate// such as the name of your company or organization.SetRegistryKey(_T("Local AppWizard-Generated Applications"));// To create the main window, this code creates a new frame window// object and then sets it as the application's main window object.CMainFrame* pFrame = new CMainFrame; // 创建窗口框架m_pMainWnd = pFrame;// create and load the frame with its resources// 在一般情况下尽量少用MFC预定义的东西做事情,因此这里用自己定义的pFrame来调用相关函数pFrame->LoadFrame(IDR_MAINFRAME,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,NULL);// The one and only window has been initialized, so show and update it.pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;}


4. 剩下的程序:

    1) 在CShapesView中添加int m_nShapes,并在构造函数中默认赋值为1,以三角形初始化;

    2) 接着添加菜单项更新和命令相应函数:

void CChildView::OnShape(UINT nID){int nShapeOrder = nID - ID_SHAPE_CIRCLE;if (nShapeOrder != m_nShape) { // 如果没变就不要重画m_nShape = nShapeOrder;Invalidate();}}void CChildView::OnUpdateShape(CCmdUI* pCmdUI){pCmdUI->SetCheck(m_nShape == (int)(pCmdUI->m_nID - ID_SHAPE_CIRCLE));}
!映射条目为ON_COMMAND_UPDATE和ON_UPDATE_COMMAND_UI;

    3) CShapesView的OnPaint:

void CChildView::OnPaint() {CPaintDC dc(this); // device context for painting// TODO: Add your message handler code here// 所有的图形都在中心的一个矩形外框中绘制CRect rcClient;GetClientRect(&rcClient);int cx = rcClient.Width() >> 1;int cy = rcClient.Height() >> 1;CRect rcShape(cx - 45, cy - 45, cx + 45, cy + 45);CPoint points[3] = { // 三角形的三个点CPoint(cx - 45, cy + 45),CPoint(cx, cy - 45),CPoint(cx + 45, cy + 45)};// 填充色CBrush brush(RGB(255, 0, 0));CBrush* pOldBrush = dc.SelectObject(&brush);switch (m_nShape){case 0: // Circledc.Ellipse(&rcShape);break;case 1: // Triangledc.Polygon(points, 3);break;case 2: // Squaredc.Rectangle(&rcShape);break;default: break;}dc.SelectObject(pOldBrush);// Do not call CWnd::OnPaint() for painting messages}

5. MFC的注释宏:

    1) 可在以下三处看到MFC的注释宏:

虚函数声明部分:关键字是//{{AFX_VIRTUAL(所在类的类名) ... //}}AFX_VIRTUAL

//{{AFX_VIRTUAL(CChildView)protected:virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//}}AFX_VIRTUAL
消息映射声明部分:关键字为//{{AFX_MSG(所在类的类名) ... //}}AFX_MSG

//{{AFX_MSG(CChildView)afx_msg void OnPaint();afx_msg void OnShape(UINT nID);afx_msg void OnUpdateShape(CCmdUI* pCmdUI);//}}AFX_MSG
消息映射部分:关键字为//{{AFX_MSG_MAP(所在类的类名) ... //}}AFX_MSG_MAP

BEGIN_MESSAGE_MAP(CChildView,CWnd )//{{AFX_MSG_MAP(CChildView)ON_WM_PAINT()ON_COMMAND_RANGE(ID_SHAPE_CIRCLE, ID_SHAPE_SQUARE, OnShape)ON_UPDATE_COMMAND_UI_RANGE(ID_SHAPE_CIRCLE, ID_SHAPE_SQUARE, OnUpdateShape)//}}AFX_MSG_MAPEND_MESSAGE_MAP()
    2) 这些宏形式特殊,其实是有特殊作用的,这些宏都是MFC特有的宏,而不是C++自身的宏,这些宏用于AppWizard代码自动生成和识别,只要将相应的内容写入MFC注释宏中,则在使用Class Wizard向导和助手时可以自动跟踪和记录这些内容,所以自己亲手添加代码时一定要将相应的内容填写到对应的注释宏中去!


!!!关于ID_APP_EXIT消息的处理:

该消息就是所有关闭应用程序的菜单项、按钮所触发的,该消息由CWinApp类接受并处理,一般不需要用户自己定义,因为这基本上是所有Windows应用程序的通用功能,其消息映射隐藏在MFC的源文件Appcore.cpp中,而该消息的响应函数定义在MFC的源文件Appui.cpp中;

0 0
原创粉丝点击