正文

第六课:菜单(一)2007-09-23 14:05:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/lym51/29611.html

分享到:

第六课:菜单(一)

一、有关菜单的一些基本知识:

1.  对于一个单文档的工程来说,菜单是在CxxxAppInitinstance中产生的(Xxx为你的工程名字):

   CSingleDocTemplate* pDocTemplate;

   pDocTemplate = new CSingleDocTemplate(

       IDR_MAINFRAME,

       RUNTIME_CLASS(CMenuDoc),

       RUNTIME_CLASS(CMainFrame),       // main SDI frame window

       RUNTIME_CLASS(CMenuView));

   AddDocTemplate(pDocTemplate);

其中IDR_MAINFRAME是菜单的ID,我们在资源面板里可以看到,很多资源的ID都是IDR_MAINFRAME,包括菜单、工具栏、加速键、图标和字符串表,所以,一个ID可以标识多个资源。需要注意的是,工具栏是在CMainFrameOnCreate函数中产生的:

if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||      !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

     TRACE0("Failed to create toolbar\n");

     return -1;      // fail to create

}

2.  当我们点击菜单时,系统发出的都是WM_COMMAND消息,在这个消息的扩展参数wParam中,包含菜单的ID,用户可以通过ID来判断是哪个菜单被点击了。

3.  在单文档工程中,MFC AppWizard为我们生成的四个类里,都可以响应同一个菜单的WM_COMMAND消息。如果在这四个类里都加了同一个菜单的响应函数,四个响应函数将只有一个被执行,也就是说,一个菜单消息只能响应被一次。它们之间有一个优先顺序,依次是:ViewàDocumentàMainFrameàApp。具体响应函数应该放在哪个类里,就要看在这个响应函数里,完成的是什么功能,放在哪个类里方便。比如,如果点击一个菜单,想隐藏工具栏,它的响应函数就应放在CMainFrame里。

4.  菜单是一格一格弹出来的,每弹出一格发出一个ON_UPDATE_COMMAND_UI消息,我们可以响应这个消息,设置菜单弹出的状态,比如打勾、打点、变灰等。注意:ON_UPDATE_COMMAND_UI只能由拥有菜单的窗口发出,切记!切记!!切切记!!!

二、菜单的操作:

我们想实现的功能是,有四个菜单:点、直线、矩形和椭圆。用户点击相应的菜单,以后用鼠标就会画出相就的图形。

1.  建立菜单:在资源面板里先建立一个弹出菜单-“画图”,再在这个弹出菜单下建四个子菜单,“点”、“直线”、“矩形”和“椭圆”,然后修改它们的ID。当我们建立一个子菜单时,系统会分配给它一个类似ID_MENUITEM32780ID。这个ID非常难记,最好把它改为一个容易记的ID,如IDM_DRAW_DOTClassWizard会根据这个ID来为菜单响应函数生成名字。另外,弹出菜单没有ID

2.  CView中加入成员变量:

CPoint m_ptOrigin;     //用于记录鼠标按下的点

int   m_nType;      //用于记录当前所画的图形

CView 的构造函数中对它们进行初始化:

    m_nType=-1;

  m_ptOrigin=0; 

3.                CView中加入菜单“点”、“直线”、“矩形”和“椭圆”的WM_COMMAND消息的响应函数。在其中设置m_nType为不同的值。

点的响应函数中:  m_nType=0;   

直线的响应函数中: m_nType=1;

矩形的响应函数中: m_nType=2;

椭圆的响应函数中: m_nType=3;

4.                CView中加入WM_LBUTTONDONWWM_LBUTTONUP的消息响应函数OnLButtonDownOnLButtonUp 。

OnLButtonDown中保存鼠标按下的点:m_ptOrigin=point;

OnLButtonUp中根据m_nType的值画相应的图形:

    CClientDC dc(this);

    switch(m_nType)

    {

    case 0:

        dc.SetPixel(point.x,point.y,RGB(255,0,0));

        break;

    case 1:

        dc.MoveTo(m_ptOrigin);

        dc.LineTo(point);

        break;

    case 2:

        dc.Rectangle(m_ptOrigin.x,m_ptOrigin.y,point.x,point.y);

        break;

    case 3:

        dc.Ellipse(m_ptOrigin.x,m_ptOrigin.y,point.x,point.y);

        break;

    default:

        break;

    }

5.                此时我们已经基本完成了要求的功能,但还无法判断当前所画的是什么图形,为了使界面更加友好,可以WM_UPDATE_COMMAND_UI消息,在当前所画的图形对应的菜单项上打勾。用ClassWizard为菜单“点”、“直线”、“矩形”和“椭圆”的ON_UPDATE_COMMAND_UI的响应函数,其形式如下:

   void CMenuView::OnUpdateDrawDot(CCmdUI* pCmdUI)

可以看到,在调用ON_UPDATE_COMMAND_UI的响应函数时,系统为用户传来了CCmdUI的指针。CCmdUI 是一个只被使用在ON_UPDATE_COMMAND_UI消息的响应函数中类,它可以控制正在被弹出的菜单项的状态。这个类中有一个成员变量m_nIndex表示菜单项在整个弹出菜单中的序号,另一个成员变量m_nID表示菜单的ID。我们来介绍一下它的成员函数:

Enable() : 设置菜单项是否有效;

SetCheck() : 设置菜单项是否打勾;

SetRadio ()  设置菜单项是否打点;

SetText() : 设置菜单项的文本。

我们就可以利用这些函数来控制菜单项的状态,比如设置当前所画的图形在对应的菜单项上打勾。

因为四个菜单的ON_UPDATE_COMMAND_UI响应函数的代码类似,我们只写出“点”的ON_UPDATE_COMMAND_UI的响应函数代码:

a.  直接判断:

if(m_nType==0)

        pCmdUI->SetCheck(true);

    else

        pCmdUI->SetCheck(false);

b. 利用m_nIndex

   pCmdUI->SetCheck(m_nType==pCmdUI->m_nIndex);

c. 利用m_nID

   pCmdUI->SetCheck(m_nType+IDM_DRAW_DOT==pCmdUI->m_nID);

   此种方法有一个条件,菜单“点”、“直线”、“矩形”和“椭圆”的ID必须是连续的。如果不连续,可以打开resource.h,将它们的ID修改为连续。

6.                快捷菜单:

CView中加入WM_RBUTTONUP的响应函数OnRButtonUp,在其中加入代码:

   ClientToScreen(&point);

GetParent()->GetMenu()->GetSubMenu(4)->TrackPopupMenu(

TPM_LEFTALIGN,point.x,point.y,GetParent());

说明:

a.     OnRButtonUp中传入的坐标是相对于窗口的,而TrackPopupMenu函数需要的是相对屏幕的坐标,所以要用ClientToScreen转换一下。

b.     如果写成

  GetMenu()->GetSubMenu(4)->TrackPopupMenu(

TPM_LEFTALIGN,point.x,point.y,GetParent());

 运行时会报红框,因为这句代码会调用CView的成员函数GetMenu(),因为CView根本就没有菜单。

c.     TrackPopupMenu函数的第四个参数指定拥有菜单的窗口,这个窗口将得到快捷菜单的所有WM_COMMAND消息。填成GetParent()CMainFrame拥有快捷菜单,此时程序没有任何问题。如果填成this,快捷菜单将不能及时的打勾。因为ON_UPDATE_COMMAND_UI只能由拥有菜单的窗口发出。

d.     我们让CMainFrame拥有快捷菜单,它得到所有WM_COMMAND消息,在CView 中加入的WM_COMMAND消息的响应函数仍然会被运行。因为对于WM_COMMAND消息存在一个转发的过程。

e.     对于快捷菜单,有一个专门的消息以响应――WM_CONTEXTMENU。如果响应它来产生快捷菜单,就不用进行ClientToScreen的坐标转换了。

f.     产生快捷菜单,也可以点击ProjectàAdd To ProjectàComponents And ControlsàVisual C++ Components , 选择Pop-up Menu

 7.给菜单项增加图标:

a.     CMainFrame中增加两个成员变量,用于装入两个位图,一个是选择菜单时的图标,一个是未被选择时的。

     CBitmap bmp1;

    CBitmap bmp2;

b.     在资源面板里增加两个位图,其IDIDB_BITMAP1IDB_BITMAP2

c.     CMainFrameOnCreate函数中加入代码:

    bmp1.LoadBitmap(IDB_BITMAP1);

      bmp2.LoadBitmap(IDB_BITMAP2);

    GetMenu()->GetSubMenu(4)->SetMenuItemBitmaps(

0,MF_BYPOSITION,&bmp1,&bmp2);

d.     此时位图可能在菜单中不能完全显示,用GetSystemMetrics()可以得到菜单图标的大小:

  CString str;

    str.Format("%d,%d",

GetSystemMetrics(SM_CXMENUCHECK),

GetSystemMetrics(SM_CYMENUCHECK));

    MessageBox(str); 


阅读(4676) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册