MyMFC(2-6)菜单 CMainFrame

本文深入解析了MFC框架中CMainFrame类的实现细节,包括窗口创建、状态栏、工具栏的配置,以及如何实现菜单项的动态操作与状态更新。详细阐述了如何通过消息映射、菜单项更新函数等技术手段,灵活地定制菜单功能与状态变化,适用于MFC框架下的Windows应用程序开发。
摘要由CSDN通过智能技术生成
 MainFrm.cpp : CMainFrame 类的实现

#include "stdafx.h"
#include "MyMFC.h"

#include "MainFrm.h"
#include "MyMFCView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	ON_WM_CREATE()
	//ON_COMMAND(IDM_TEST, &CMainFrame::OnTest)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, &CMainFrame::OnUpdateEditCut) //增加消息映射,交给OnUpdateEditCut函数来处理
	ON_COMMAND(IDM_HELLO,OnHello)
END_MESSAGE_MAP()

static UINT indicators[] =
{
	ID_SEPARATOR,           // 状态行指示器
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
};

// CMainFrame 构造/析构

CMainFrame::CMainFrame()
{
	// TODO:  在此添加成员初始化代码
//	m_bAutoMenuEnable = FALSE; //这样之后才能够把菜单项改变为禁用和灰色。一般情况下不会去设置它的。
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1) //调用CFrameWnd::OnCreate函数创建一个窗口,然后创建工具条(m_wndToolBar),状态栏(m_wndStatusBar)
		return -1;

	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("未能创建工具栏\n");
		return -1;      // 未能创建
	}

	if (!m_wndStatusBar.Create(this))
	{
		TRACE0("未能创建状态栏\n");
		return -1;      // 未能创建
	}
	m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));

	// TODO:  如果不需要可停靠工具栏,则删除这三行
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);

/************************************************************************************************************/
//创建按钮
//Create函数中参数:
//1.按钮的名称
//2.按钮的控件风格
//3.按钮控件的大小和位置。该参数是RECT结构体,通过定义左上角和右下角两个点的坐标来定义一个矩形。(左上x,左上y,右下x,右下y)
//4.指定按钮控件的父窗口,这是一个CWnd类型的指针。MFC中不再通过窗口句柄,而是通过一个与窗口相关的C++窗口类对象指针来传递窗口对象
//5.指定按钮控件的表示.(自己取一个整数即可)


//	m_btn.Create((CString)"按钮", WS_CHILD | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), this, 123); //由于this指针指定的是 CMainFrame类,产生在(0, 0, 100, 100)处,会覆盖工具栏
//	m_btn.ShowWindow(SW_SHOWNORMAL);



/**************************************************************************************************************/
	//创建标记菜单,即在菜单项的前面打一个对勾。也可以用添加UPDATE_COMMAND_UI消息来实现
	//因为主菜单的程序属于框架窗口,所以在框架类中编写
	//首先用GetMenu函数获得指向菜单栏的指针
	//GetSubMenu(n)这个成员函数可以获得子菜单的指针,参数n代表子菜单的索引号。###注:子菜单只能通过索引号来访问,它没有ID号
	//CheckMenuItem函数可以为菜单添加或移除菜单项的标记。它的第一个参数与第二个参数有关,如果第二个参数有含有MF_BYPOSITION
	//那么第一个参数为菜单项的索引号;如果第二个参数含有MF_BYCOMMAND,那么第一个参数为菜单项的命令ID
	//用索引号的程序
	//GetMenu()->GetSubMenu(0)->CheckMenuItem(0, MF_BYPOSITION | MF_CHECKED); //MF_CHECKED设置标记;MF_UNCHECKED取消标记
	//或用ID号的程序
	GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW, MF_BYCOMMAND | MF_CHECKED);



/**************************************************************************************************/
	//设置默认菜单项,即把菜单项加粗。一个子菜单只能有一个默认菜单项
	GetMenu()->GetSubMenu(0)->SetDefaultItem(1, TRUE); //第二个参数为TRUE,则第一个参数为索引号。第二个参数默认为FALSE,第一个参数为ID号


	//索引号是包含分隔栏的。


/**********************************************************************************************************/
	//图像标记菜单,即在菜单项的前面加一个小图标,而不是对勾
	//可以用SetMenuItemBitmaps函数将制定的位图以菜单关联起来,参数含义:
	//1,2第一个参数与第二个参数有关。索引号,ID号;MF_BYCOMMAND,MF_BYPOSITION
	//3.指定当取消菜单选项中状态时的位图
	//4.指定选中菜单项时显示的位图
	//但是所做位图的大小是有限制的,若是太大,则不会显示出来,
	//用GetSystemMetrics函数可以获得菜单项上显示位图的尺寸,参数为SM_CXMENUCHECK,则获得图像的宽度,参数为SM_CYMENUCHECK,则获得图像的高度



/**********************************************************************************************************************/
/*	//获得所要创建位图的大小
	CString str;
	str.Format(_T("x=%d,y=%d"),GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
	MessageBox(str);  */
		m_bitmap.LoadBitmapW(IDB_BITMAP3);
	GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(2, MF_BYPOSITION, &m_bitmap, NULL);



/**************************************************************************************************************************/
	//设置禁用菜单项(一般不用这种方法,而用下下面的方法,因为这个方法会影响别的菜单项的状态)
	//使用EnableMenuItem函数可以设置菜单项为:能够使用(MF_ENABLE)、禁用(MF_DISABLE)、变灰(MF_GRAYED)。
	//由于MFC为菜单提供了一种命令更新的机制,程序在运行时,根据此机制去判断哪个菜单可以使用,哪个菜单不能够使用,然后显示其相应的状态。
	//如果想自己更改菜单项的状态,必须先再框架类的构造函数中把m_bAutoMenuEnable变量设为FALSE,之后自己对菜单项的状态更改才会起作用。
	//但是当把m_bAutoMenuEnalbe=FALSE后,之前自动变灰的、禁用的菜单项就不再是灰色显示了。因为MFC就不再利用更新机制去判断哪个菜单可以使用
	//哪个菜单不能使用了。都需要自己去完成。

	//GetMenu()->GetSubMenu(0)->EnableMenuItem(6, MF_BYPOSITION | MF_DISABLED); //第一个参数由第二个参数决定



/******************************************************************************************************************/
/*	//移除和装载菜单
	SetMenu(NULL);  //参数为一个指向菜单对象的指针CMenu*。如果为NULL,这表示移除当前菜单

	CMenu menu;
	menu.LoadMenu(IDR_MAINFRAME);
	SetMenu(&menu); //因为menu是一个局部变量,虽然可以产生窗口,但是程序会有问题。可以把menu设为一个成员函数,还可以用Detach函数把菜单句柄与菜单对象分离
	//这样,当这个局部菜单对象的生命周期结束时,它不会去销毁一个它不再具有拥有权的菜单
	menu.Detach();  */



/**********************************************************************************************************************/
	//利用MFC编程时,菜单项状态的维护依赖于CN_UPDATE_COMMAND_UI消息,一般要改变菜单项的状态,只要捕获UPDATE_COMMAND_UI消息
	//在该消息的响应函数中调用CCmdUI对象的响应函数。
	//所以要在相应的菜单项上面建立UPDATE_COMMAND_UI消息相应,在菜单项上右击-添加事件处理程序-在消息类型中选则UPDATE_COMMAND_UI,再在相应的函数中编程来实现相应的功能
	//在下面的void CMainFrame::OnUpdateEditCut(CCmdUI *pCmdUI)函数中,来实现编辑-剪切的禁用、使用、标记等功能


//###综上所述:如果要在程序中设置某个菜单项的状态,通过添加UPDATE_COMMAND_UI消息相应,在相应的函数中进行状态设置即可###。

	
/****************************************************************************************************************************/
	//动态菜单操作
	//我们可以在资源视图-Menu中通过鼠标对菜单进行添加、插入、移动、删除操作,和右击-添加事件处理程序。但这些都是已有的菜单和菜单项
	//我们可以使用指令来实现这些操作,动态操作分为:弹出菜单的动态操作、菜单项的动态操作
	CMenu menu;
	menu.CreateMenu();
	//添加子菜单
	GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, CString("动态")); //在子菜单的后面添加一个子菜单
	menu.Detach(); //因为menu是局部变量

	CMenu menu1;
	menu1.CreateMenu();
	//插入子菜单
	GetMenu()->InsertMenu(2, MF_POPUP | MF_BYPOSITION, (UINT)menu1.m_hMenu, CString("动态插入")); // 在索引号2之前插入一个子菜单
	//添加菜单项
	menu1.AppendMenu(MF_STRING, 111, CString("HELLO"));  //111是自己为菜单项赋予的一个ID号
	menu1.AppendMenu(MF_STRING, 112, CString("Bye"));
	GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 113, CString("Welcome"));
	//插入菜单项
	GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN, MF_BYCOMMAND | MF_STRING, 114, CString("VC 编程"));
	menu1.Detach();

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



/************************************/
	//动态添加菜单项的命令响应
	//菜单命令消息的响应的步骤:
	//1.由于前面动态添加的菜单项,并不会产生ID号。那我们就要定义ID号。在解决方案资源-头文件-Resource.h中定义了程序当前使用的资源ID,可以在这里面人工的添加新的ID
	//如:#define IDM_HELLO	 111 这里的111是在上面动态创建Hello菜单项时所自定义的ID。IDM_HELLO是现在自定义的名称,因为要在步骤3中会用到
	//2.在响应这个菜单项命令的程序类的头文件中添加响应函数原型。如在MainFrm.h中添加public:	afx_msg void OnHello();
	//3.在响应这个菜单项命令的程序类的源文件中的消息映射表中添加消息映射。如在本.cpp中添加ON_COMMAND(IDM_HELLO,OnHello)记住:没有分号
	//4.实现菜单命令消息响应函数的定义体。如下面定义的OnHello函数 

	return 0;
}
void CMainFrame::OnHello()
{
	MessageBox(CString("动态添加菜单项的命令响应"));
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) 
{//改变CREATRSTRUCT结构体的值,那么调用CreateWindowEx函数时的参数也会发生相应的改变,从而创造出符合我们要求的窗口.(改变窗口的外观)
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	// TODO:  在此处通过修改


/******************************************************************************************************************************************************/
	//  CREATESTRUCT cs 来修改窗口类或样式
	// CRREATRSTRUCT结构体中的数据成员与CreateWindowExpect函数中的参数一致,(###只是顺序相反###)
	//CreateWindow函数有11个参数  
	//1.lpszName指定窗口类的名称,即为你在第一步中设计窗口类的名称。表示要产生这一类型的窗口,是注册过的窗口名  
	//2.lpszName指定标题栏的名称  
	//*3.style创建窗口的样式。注意与WNDCLASS中的style成员与CreateWindow函数的dwstyle参数不同,前者是指定窗口类的样式,基于该窗口类创建的窗口都具有这样的样式,  
	//后者是指定某个具体的窗口的样式,如:创建(###一个有标题栏和边框的窗口###)、带有系统菜单的窗口、(###具有可调边框的窗口###)、具有最小化按钮、最大化按钮的窗口  
	//WS_OVERLAPPEDWINDOW参数是所有样式的综合,利用取反~,值&,可以去掉一些功能,如&~WS_THICKFRAME则,窗口不能调节大小了  
	//4,5.x,y指定窗口左上角的x,y坐标。(即出现在屏幕上的位置)  
	//6,7.cx,cy指定窗口的宽度、高度。若任意一个设为CW_USEDEFAULT,系统默认宽高,另外一个参数必须省略  
	//**8.hwndParaent指定被创建窗口的父窗口句柄,当窗口中还有窗口时,必须设置,该参数是HWDN类型的  
	//9.hMenu指定窗口菜单的句柄,可能与上面的参数lpszClassName有关  
	//10.hInstancd指定窗口所属的应用程序实例的句柄,由WinMain函数参数提供的  
	//11.lpCreatrParams作为WM_CREATE消息的附加参数lparam传入的数据指针。在创建多文档界面的客户窗口时,lpParam必须指向CLIENTCREATESTRUCT.一般设为NULL  
	//多了一个参数 dwExStyle
    //顺序相反
	
	return TRUE;
}

// CMainFrame 诊断

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
	CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
	CFrameWnd::Dump(dc);
}
#endif //_DEBUG


// CMainFrame 消息处理程序



//void CMainFrame::OnTest()
//{
//	// TODO:  在此添加命令处理程序代码
//	MessageBox((CString)"MainFrame Clicked");
//
//}


//void CMainFrame::OnTest()
//{
//	// TODO:  在此添加命令处理程序代码
//	MessageBox((CString)"MainFrame Clicked");
//}


void CMainFrame::OnUpdateEditCut(CCmdUI *pCmdUI)
{
	// TODO:  在此添加命令更新用户界面处理程序代码

/************************************************************************************/
	//###UPDATE_COMMAND_UI消息的响应只能应用于菜单项,不能应用于弹出式菜单###
	//调用CCmdUI对象相应的函数,如:Enable(可用或禁用)、SetCheck(设置标记菜单)、SteText(设置菜单项的文本)
	pCmdUI->Enable(TRUE); //TRUE可用、FALSE禁用
	//发现工具栏上剪切工具按钮也可以使用。工具栏上的一个工具按钮与菜单栏中的某个菜单项的ID是相同的,所以它们关联在一起
	pCmdUI->SetCheck();  //前面产生对勾标记
	
}


/******************************************************************************************************************************************************/
//因为菜单项的命令响应,是由View类先响应的。若想让框架类来截获,怎么办?因为菜单命令是交由OnCommand函数来处理的,###在这个函数中将完成命令响应的路由###
//又因为OnCommand函数时虚函数,所以我们可以重写这个函数,来截获菜单的命令消息,不让它们再继续向下路由,处理完后,最后再调用基类的OnCommand函数进行消息的路由
//ONCommand函数是对所有的命令消息进行路由处理的
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) //参数wParam的低端的两个字节放置的是发送当前消息的菜单项、工具按钮、加速键的命令ID
														 //用LOWORD这个宏可以取得当前消息命令的ID
{
	// TODO:  在此添加专用代码和/或调用基类
	int MenuCmdID = LOWORD(wParam);  //用LOWORD这个宏可以取得当前消息命令的ID
	CMyMFCView *pView = (CMyMFCView*)GetActiveView();  //在框架类中获得视类的指针。定义了CMyMFCView类型,所以头文件中应添加#include "MyMFCView.h"
	if (MenuCmdID >= IDM_PHONE1&&MenuCmdID < IDM_PHONE1 + pView->m_strArray.GetSize())
	{
		MessageBox(CString("Test"));
		CClientDC dc(pView);
		dc.TextOut(0, 0, pView->m_strArray.GetAt(MenuCmdID - IDM_PHONE1));
		return TRUE;  //这样就不会再调用基类中的OnCommand了
	}

	return CFrameWnd::OnCommand(wParam, lParam);
}


<pre name="code" class="cpp">

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值