孙鑫VC学习(第6课--菜单)

6课,菜单及与菜单相关的编程:

新建一个单文档MFC程序。

消息响应的四个顺序:VIEW,DOC类,框架类,APP类。

n       标准消息

      WM_COMMAND之外,所有以WM_开头的消息。

      CWnd派生的类,都可以接收到这类消息。

n       命令消息

      来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。

       CCmdTarget派生的类,都可以接收到这类消息。

n       通告消息

      由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。

       CCmdTarget派生的类,都可以接收到这类消息。

CWnd派生的类可以接收标准消息和命令消息和通告消息。

菜单消息路由过程:view—doc—view—frame—app

下面是菜单:

子菜单,菜单项。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中添加:

GetMenu()->GetSubMenu(0)->CheckMenuItem( 0, MF_BYPOSITION|MF_CHECKED );

此句是给文件/新建做个标记。

下面介绍缺省菜单项:

GetMenu()->GetSubMenu(0)->SetDefaultItem( 1,TRUE);缺省项为粗体显示。缺省项在一个子菜单中只能设置一个,分格栏也占一个ID号。

下面创建一个图形标记菜单:

CBitmap *bp1=new CBitmap;

       CBitmap *bp2=new CBitmap;

       bp1->LoadBitmap(IDB_BITMAP1);

       bp2->LoadBitmap(IDB_BITMAP2);

    //下面的程序用来得到标记图标的大小,以此来做图

//    int x=GetSystemMetrics(SM_CXMENUCHECK);

//    int y=GetSystemMetrics(SM_CYMENUCHECK);

//    CString str;

//    str.Format("x=%d,y=%d",x,y);

//    MessageBox(str);//宽度和高度都应为13

 

       GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(1,MF_BYPOSITION,bp1,bp2);

 

下面让菜单项不能使用。          

GetMenu()->GetSubMenu(0)->EnableMenuItem(ID_FILE_OPEN,

              MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);

当然要加上下面的代码,否则程序不能运行正常,达到预期效果:

CMainFrame::CMainFrame()

{

       // TODO: add member initialization code here

       m_bAutoMenuEnable=FALSE;

}

但是m_bAutoMenuEnable不建议改变它的值,因为它是系统自动改动的。

将整个菜单取消掉:

SetMenu( NULL );

下面是再创建:

       CMenu menu;

       menu.LoadMenu(IDR_MAINFRAME);

       SetMenu(&menu);

       menu.Detach();

MFC对菜单项所采用的命令更新机制。

原先在菜单中的剪切菜单项是不能用的,下面代码让它可以使用。(CN_UPDATE_COMMAND_UI

void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)

{

       // TODO: Add your command update UI handler code here

       pCmdUI->Enable();

}

更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。

下面创建一个右键弹出菜单功能:通过插入右键弹出控件

也可以自己创建,先创建一个菜单项。

 

void CTestView::OnRButtonDown(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler code here and/or call default

      

       CMenu menu;

       menu.LoadMenu(IDR_MENU1);

       CMenu *pmenu=menu.GetSubMenu(0);

       ClientToScreen(&point);

       pmenu->TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x+5,

              point.y+5, this);

       CView::OnRButtonDown(nFlags, point);

}

此时弹出菜单的菜单项在框架类中是不能响应的。因为弹出菜单的拥有者是VIEW类,因此只能是VIEW类对弹出菜单进行响应。如果要想让框架类对弹出菜单也进行响应,要修改弹出菜单的拥有者窗口。

 

下面动态添加,删除菜单。

为框架类增加:

private:

       CMenu m_popmenu;

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中:

m_popmenu.CreatePopupMenu();

    GetMenu()->AppendMenu(MF_POPUP,(UINT)m_popmenu.m_hMenu,"popupmenu");

 

GetMenu()->InsertMenu(1,MF_BYPOSITION,(UINT)m_popmenu.m_hMenu,"insertmenu");

在资源中的STRING项中增加:IDM_POPMENU1--IDM_POPMENU3的说明。

       m_popmenu.AppendMenu(MF_STRING,IDM_POPMENU1,"popmenu1");

       m_popmenu.AppendMenu(MF_STRING,IDM_POPMENU2,"popmenu2");

       m_popmenu.AppendMenu(MF_STRING,IDM_POPMENU3,"popmenu3");

GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,111,"filemenu3");

GetMenu()->GetSubMenu(0)->InsertMenu(2,MF_BYPOSITION|MF_STRING,112,"fileinsert");插入菜单。

下面删除菜单:

GetMenu()->DeleteMenu(1,MF_BYPOSITION);

GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);//删除菜单子项。

现在对这些动态创建的菜单进行命令响应。

GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,111,"filemenu3");

对这个111进行响应。因为没有对它进行ID号的说明,要自己手动添加:

Resource.h中定义如下:

#define IDM_FILEMENU3                   111 //这样的话,就有了一个ID号。

然后将此句改成有ID号的:

GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,IDM_FILEMENU3,"filemenu3");

在框架类的头文件中加上:afx_msg void OnFileMenu3();

在框架类的源文件中:ON_COMMAND(IDM_FILEMENU3,OnFileMenu3)

void CMainFrame::OnFileMenu3()

{

    MessageBox("Onfilemenu3");

}

这样就实现了函数的实现。

下面对IDM_POPMENU1(我们自己给出的ID号的菜单增加命令响应)

和上面的实现一样:三步骤:头文件中一处,源文件中两处。

下面编写一个电话本程序:

输入一个人名+空格+电话号码,动态增加菜单,人名作为动态菜单项增加进去,当点击增加的弹出菜单时,显示人名+空格+电话号码。

CTestView增加:

private:

       int m_index; 初始化为0

       CMenu m_menu;

CString m_str;

public:

CStringArray m_strArray;

给菜单添加四个菜单项,分别是IDM_PHONE1---IDM_PHONE4,在Resource.h中定义它们的值。

#define IDM_PHONE1                      201

#define IDM_PHONE2                      202

#define IDM_PHONE3                      203

#define IDM_PHONE4                      204

VIEW类中添加消息响应。然后把它们删除。

这是在VIEW类的源文件中:

BEGIN_MESSAGE_MAP(CTestView, CView)

       //{{AFX_MSG_MAP(CTestView)

       ON_WM_CHAR()

       ON_COMMAND(IDM_PHONE1, OnPhone1)

       ON_COMMAND(IDM_PHONE2, OnPhone2)

       ON_COMMAND(IDM_PHONE3, OnPhone3)

       ON_COMMAND(IDM_PHONE4, OnPhone4)

       //}}AFX_MSG_MAP

       // Standard printing commands

       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()

这样是不行的,因为是系统自己加的对IDM_PHONE的响应,所以在中间。我们把它们拿出来,改成下面的形式:

BEGIN_MESSAGE_MAP(CTestView, CView)

       //{{AFX_MSG_MAP(CTestView)

       ON_WM_CHAR()

      

       //}}AFX_MSG_MAP

       // Standard printing commands

       ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

       ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

       ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

       ON_COMMAND(IDM_PHONE1, OnPhone1)

       ON_COMMAND(IDM_PHONE2, OnPhone2)

       ON_COMMAND(IDM_PHONE3, OnPhone3)

       ON_COMMAND(IDM_PHONE4, OnPhone4)

END_MESSAGE_MAP()

此时CLASSWIZARD中是看不到IDM_PHONE的。(在这里一定要拿出来,因为刚刚是CLASSWIZARD自己生成的。因为我们把菜单已经删除了,帮CLASSWIZARD会将命令响应删除)。头文件中可以拿出来,也可以不拿出来。

void CTestView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

{

       // TODO: Add your message handler code here and/or call default

       CClientDC dc(this);

       if (0X0d==nChar)

       {

              if (m_index++==0)//第一次回车

              {

                     m_menu.CreatePopupMenu();

                     GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"notebook");

                     GetParent()->DrawMenuBar();//当动态改变菜单时,要对菜单项进行重绘

 

              }

              int spaceaddr=m_str.Find(' ');

              CString strname=m_str.Left(spaceaddr);

              m_menu.AppendMenu(MF_STRING,IDM_PHONE1+(m_index-1),strname);

              m_strArray.Add(m_str);

        m_str.Empty();

              Invalidate();//擦除窗口

       }

       else//输入数据

       {

              m_str+=nChar;

              dc.TextOut(0,0,m_str);

       }

       CView::OnChar(nChar, nRepCnt, nFlags);

}

void CTestView::OnPhone1()

{

       // TODO: Add your command handler code here

       CClientDC dc(this);

       dc.TextOut(0,0,m_strArray.GetAt(0));

}

 

void CTestView::OnPhone2()

{

       // TODO: Add your command handler code here

       CClientDC dc(this);

       dc.TextOut(0,0,m_strArray.GetAt(1));

}

 

void CTestView::OnPhone3()

{

       // TODO: Add your command handler code here

       CClientDC dc(this);

       dc.TextOut(0,0,m_strArray.GetAt(2));

}

 

void CTestView::OnPhone4()

{

       // TODO: Add your command handler code here

       CClientDC dc(this);

       dc.TextOut(0,0,m_strArray.GetAt(3));

}

当然了,如果你想要显示更多 ,就可以依照刚刚的方法,来添加phone5,phone6。。。。。。

下面对菜单的响应由框架类响应。;

根据消息漏由,先由框架类交给VIEW类。

virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam );

The framework calls this member function when the user selects an item from a menu, when a child control sends a notification message, or when an accelerator keystroke is translated.

给框架类的头文件加上  #include "TestView.h"(源文件参与编译,头文件不参与)

 

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)

{

       CTestView *pView = (CTestView*)GetActiveView();

       if (LOWORD(wParam)<IDM_PHONE1+pView->m_strArray.GetSize() && LOWORD(wParam)>=IDM_PHONE1)

       {

        CClientDC dc(pView);

              dc.TextOut(0,0,pView->m_strArray.GetAt(LOWORD(wParam)-IDM_PHONE1));

              return TRUE;

       }

       return CFrameWnd::OnCommand(wParam,lParam);

}

在框架类当中去截获本身最先就由VIEW类响应的消息。(通过增加OnCommand来处理)。

 

 

 

你可能感兴趣的:(孙鑫VC学习(第6课--菜单))