精通 VC++ 实效编程280例 - 02 菜单和光标
添加时间:2013-6-11 点击量:
菜单和封闭时首要的 Windows 资料之一。SDK 中,用 HCURSOR 和 HMENU 分别默示菜单和光标的句柄。MFC 中,CMenu 类封装了菜单的功能。
23 动态添加和删除菜单项
添加菜单项可以调用 CMenu::AppendMenu 或 CMenu::InserMenu 函数,删除菜单项可以调用 CMenu::RemoveMenu 或 CMenu::DeleteMenu 函数,最后调用 CWnd::DrawMenuBar 函数重画菜单。
- CMenu::AppendMenu:在菜单末尾添加菜单项。
- CMenu::InserMenu:在菜单指定地位添加菜单项。
- CMenu::RemoveMenu:移动菜单项,若是菜单项与弹出菜单相接洽关系,将不将烧毁弹出菜单的句柄,是以菜单可重用。
- CMenu::DeleteMenu:删除菜单项,若是菜单项与弹出菜单相接洽关系,将烧毁弹出菜单的句柄,并开释其占用的内存。
#define ID_TEST_MENU 10000
void CMainFrame::OnAppendMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 4)
{
//在菜单末尾添加菜单项
pSubMenu->AppendMenu(MF_STRING,ID_TEST_MENU,_T(New Menu));
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnInsertMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 4)
{
//在菜单指定地位添加菜单项
pSubMenu->InsertMenu(4,MF_BYPOSITION,ID_TEST_MENU,_T(New Menu));
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnRemoveMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 5)
{
//删除菜单项
pSubMenu->RemoveMenu(4,MF_BYPOSITION);
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnDeleteMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 5)
{
//删除菜单项
pSubMenu->DeleteMenu(4,MF_BYPOSITION);
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnTestMenu()
{
AfxMessageBox(_T(测试菜单项号令));
}
24 在体系菜单中添加和删除菜单项
在体系菜单中添加和删除菜单项,起首调用 CWnd::GetSystemMenu 函数获得体系菜单的指针,然后调用 CMenu::AppendMenu 或 CMenu::InsertMenu,CMenu::RemoveMenu 或 CMenu::DeleteMenu 函数添加和删除菜单项,最后调用 CWnd::DrawMenuBar 函数重画菜单。对于添加的菜单项,可以在 CWnd::OnSysCommand 重载函数中处理惩罚菜单号令。
//在resource.h文件中添加宏定义
#define ID_TEST_MENU 10000
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//...
//获得体系菜单
CMenu pMenu = GetSystemMenu(FALSE);
//删除体系菜单项
pMenu->RemoveMenu(SC_MOVE,MF_STRING);
//添加菜单项
pMenu->InsertMenu(0,MF_BYPOSITION,ID_TEST_MENU,_T(New Menu));
//重画菜单
DrawMenuBar();
return TRUE;
}
void CDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0 xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else if(nID == ID_TEST_MENU)
{
AfxMessageBox(_T(测试菜单项号令));
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
25 禁用封闭按钮
禁用封闭按钮可以调用 CMenu::EnableMenuItem 函数。
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//...
//获得体系菜单
CMenu pMenu = GetSystemMenu(FALSE);
//禁用封闭按钮
pMenu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND | MF_GRAYED);
return TRUE;
}
26 启用和禁用菜单项
可以在类的 UPDATE_COMMAND_UI 消息处理惩罚函数中调用 CCmdUI::Enable 函数,启用和禁用菜单项。
//在CMainFrame类中添加成员变量
public:
BOOL m_bEnable1;
BOOL m_bEnable2;
//在CMainFrame类的机关函数中初始化成员变量
CMainFrame::CMainFrame()
{
m_bEnable1 = TRUE;
m_bEnable2 = FALSE;
}
//在CMainFrame类中为菜单项添加号令处理惩罚函数
void CMainFrame::OnTestMenu1()
{
m_bEnable1 = FALSE;
m_bEnable2 = TRUE;
}
void CMainFrame::OnUpdateTestMenu1(CCmdUI pCmdUI)
{
//启动或禁用菜单1
pCmdUI->Enable(m_bEnable1);
}
void CMainFrame::OnTestMenu2()
{
m_bEnable1 = TRUE;
m_bEnable2 = FALSE;
}
void CMainFrame::OnUpdateTestMenu2(CCmdUI pCmdUI)
{
//启动或禁用菜单2
pCmdUI->Enable(m_bEnable2);
}
27 设置菜单项的搜检状况
可以在类的 UPDATE_COMMAND_UI 消息处理惩罚函数中调用 CCmdUI::SetCheck 函数设置菜单项的搜检状况(选中/不选中状况)。
//在CMainFrame类中添加成员变量
public:
int m_nCheck;
//在CMainFrame类的机关函数中初始化成员变量
CMainFrame::CMainFrame()
{
m_nCheck = 0;
}
//在CMainFrame类中为菜单项添加号令处理惩罚函数
void CMainFrame::OnTestMenu()
{
if (m_nCheck == 0)
{
m_nCheck = 1;
}
else
{
m_nCheck = 0;
}
}
void CMainFrame::OnUpdateTestMenu(CCmdUI pCmdUI)
{
//设置菜单项搜检状况
pCmdUI->SetCheck(m_nCheck);
}
28 快捷菜单
实现快捷菜单可以在 CWnd::OnContextMenu 重载函数中调用 CMenu::TrackPopupMenu 函数。
在资料中添加1个菜单资料,ID 为 IDR_MENU。在菜单资料中添加1个子菜单,并添加菜单项。
//在CDemoView类中重载CWnd::OnContextMenu函数
void CDemoView::OnContextMenu(CWnd pWnd, CPoint point)
{
CMenu menu;
//加载菜单
if (!menu.LoadMenu(IDR_MENU))
{
return;
}
//获得子菜单
CMenu pPopupMenu = menu.GetSubMenu(0);
//弹出菜单
pPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y,pWnd);
}
void CDemoView::OnTestMenu1()
{
AfxMessageBox(_T(快捷菜单项1号令));
}
void CDemoView::OnTestMenu2()
{
AfxMessageBox(_T(快捷菜单项2号令));
}
29 获取光标的坐标
可以在 WM_MOUSEMOVE 消息处理惩罚函数中获得光标的地位。
//在CDemoView类中添加成员变量
public:
CPoint m_Point;
//在类中添加WM_MOUSEMOVE消息处理惩罚函数
void CDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
//保存光标坐标
m_Point = point;
//刷新客户区
Invalidate();
CView::OnMouseMove(nFlags, point);
}
//在类中重载CView::OnDraw函数
void CDemoView::OnDraw(CDC pDC)
{
//获得客户区坐标
CRect rect;
GetClientRect(rect);
//绘制十字光标
pDC->MoveTo(0,m_Point.y);
pDC->LineTo(rect.Width(),m_Point.y);
pDC->MoveTo(m_Point.x,0);
pDC->LineTo(m_Point.x,rect.Height());
//输出光标坐标
CString strText = _T();
strText.Format(_T(%d,%d),m_Point.x,m_Point.y);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextAlign(TA_RIGHT | TA_BOTTOM);
pDC->TextOut(m_Point.x,m_Point.y,strText);
}
30 限制光标的移动局限
限制光标的局限可以调用 SDK 的 ClipCursor 函数。
//在CDemoView类中重载CView::OnDraw函数
void CDemoView::OnDraw(CDC pDC)
{
//获得客户区坐标
CRect rect;
GetClientRect(rect);
rect.left = rect.left + rect.Width() / 4;
rect.right = rect.right - rect.Width() / 4;
rect.top = rect.top + rect.Height() / 4;
rect.bottom = rect.bottom -rect.Height() / 4;
//绘制光标移动局限
pDC->Rectangle(rect);
}
//在CDemoView类平分别添加WM_LBUTTONDOWN和WM_LBUTTONUP消息处理惩罚函数
void CDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
//获得客户区坐标
CRect rect;
GetClientRect(rect);
rect.left = rect.left + rect.Width() / 4;
rect.right = rect.right - rect.Width() / 4;
rect.top = rect.top + rect.Height() / 4;
rect.bottom = rect.bottom - rect.Height() /4;
//映射屏幕坐标
ClientToScreen(rect);
//限制光标移动局限
ClipCursor(&rect);
CView::OnLButtonDown(nFlags, point);
}
void CDemoView::OnLButtonUp(UINT nFlags, CPoint point)
{
//光标移动
ClipCursor(NULL);
CView::OnLButtonUp(nFlags, point);
}
31 自定义光标
应用自定义光标,起首调用 CWinApp::LoadCursor函数加载光标,然后调用SDK的SetCursor函数设置光标。
//创建1个单文档的应用法度,并添加2个光标资料,在CDemoView类中添加成员变量
public:
HCURSOR m_hCursor;
//在CDemoView类中重载CView::OnInitialUpdate函数
void CDemo2View::OnInitialUpdate()
{
CView::OnInitialUpdate();
//加载光标
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR1);
}
//在CDemoView类中添加WM_SETCURSOR消息处理惩罚函数
BOOL CDemo2View::OnSetCursor(CWnd pWnd, UINT nHitTest, UINT message)
{
//设置光标
::SetCursor(m_hCursor);
return TRUE;
}
//在CDemoView类平分别添加WM_LBUTTONDOWN和WM_LBUTTONUP消息处理惩罚函数
void CDemo2View::OnLButtonDown(UINT nFlags, CPoint point)
{
//加载光标
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR2);
//设置光标
::SetCursor(m_hCursor);
CView::OnLButtonDown(nFlags, point);
}
void CDemo2View::OnLButtonUp(UINT nFlags, CPoint point)
{
//加载光标
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR1);
//设置光标
::SetCursor(m_hCursor);
CView::OnLButtonUp(nFlags, point);
}
32 守候光标
启动守候光标可以调用 CCmdTarget::BeginWaitCursor函数,停止守候光标可以调用CCmdTarget::EndWaitCursor函数。
void CDemo2View::OnLButtonUp(UINT nFlags, CPoint point)
{
//启动守候光标
BeginWaitCursor();
//休眠
Sleep(5000);
//停止守候光标
EndWaitCursor();
CView::OnLButtonDown(nFlags,point);
}
真正的心灵世界会告诉你根本看不见的东西,这东西需要你付出思想和灵魂的劳动去获取,然后它会照亮你的生命,永远照亮你的生命。——王安忆《小说家的十三堂课》
菜单和封闭时首要的 Windows 资料之一。SDK 中,用 HCURSOR 和 HMENU 分别默示菜单和光标的句柄。MFC 中,CMenu 类封装了菜单的功能。
23 动态添加和删除菜单项
添加菜单项可以调用 CMenu::AppendMenu 或 CMenu::InserMenu 函数,删除菜单项可以调用 CMenu::RemoveMenu 或 CMenu::DeleteMenu 函数,最后调用 CWnd::DrawMenuBar 函数重画菜单。
- CMenu::AppendMenu:在菜单末尾添加菜单项。
- CMenu::InserMenu:在菜单指定地位添加菜单项。
- CMenu::RemoveMenu:移动菜单项,若是菜单项与弹出菜单相接洽关系,将不将烧毁弹出菜单的句柄,是以菜单可重用。
- CMenu::DeleteMenu:删除菜单项,若是菜单项与弹出菜单相接洽关系,将烧毁弹出菜单的句柄,并开释其占用的内存。
#define ID_TEST_MENU 10000
void CMainFrame::OnAppendMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 4)
{
//在菜单末尾添加菜单项
pSubMenu->AppendMenu(MF_STRING,ID_TEST_MENU,_T(New Menu));
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnInsertMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 4)
{
//在菜单指定地位添加菜单项
pSubMenu->InsertMenu(4,MF_BYPOSITION,ID_TEST_MENU,_T(New Menu));
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnRemoveMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 5)
{
//删除菜单项
pSubMenu->RemoveMenu(4,MF_BYPOSITION);
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnDeleteMenu()
{
//获得主菜单
CMenu pMenu = GetMenu();
//获得子菜单
CMenu pSubMenu = pMenu->GetSubMenu(4);
if (pSubMenu->GetMenuItemCount() == 5)
{
//删除菜单项
pSubMenu->DeleteMenu(4,MF_BYPOSITION);
//重画菜单
DrawMenuBar();
}
}
void CMainFrame::OnTestMenu()
{
AfxMessageBox(_T(测试菜单项号令));
}
24 在体系菜单中添加和删除菜单项
在体系菜单中添加和删除菜单项,起首调用 CWnd::GetSystemMenu 函数获得体系菜单的指针,然后调用 CMenu::AppendMenu 或 CMenu::InsertMenu,CMenu::RemoveMenu 或 CMenu::DeleteMenu 函数添加和删除菜单项,最后调用 CWnd::DrawMenuBar 函数重画菜单。对于添加的菜单项,可以在 CWnd::OnSysCommand 重载函数中处理惩罚菜单号令。
//在resource.h文件中添加宏定义
#define ID_TEST_MENU 10000
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//...
//获得体系菜单
CMenu pMenu = GetSystemMenu(FALSE);
//删除体系菜单项
pMenu->RemoveMenu(SC_MOVE,MF_STRING);
//添加菜单项
pMenu->InsertMenu(0,MF_BYPOSITION,ID_TEST_MENU,_T(New Menu));
//重画菜单
DrawMenuBar();
return TRUE;
}
void CDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0 xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else if(nID == ID_TEST_MENU)
{
AfxMessageBox(_T(测试菜单项号令));
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
25 禁用封闭按钮
禁用封闭按钮可以调用 CMenu::EnableMenuItem 函数。
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//...
//获得体系菜单
CMenu pMenu = GetSystemMenu(FALSE);
//禁用封闭按钮
pMenu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND | MF_GRAYED);
return TRUE;
}
26 启用和禁用菜单项
可以在类的 UPDATE_COMMAND_UI 消息处理惩罚函数中调用 CCmdUI::Enable 函数,启用和禁用菜单项。
//在CMainFrame类中添加成员变量
public:
BOOL m_bEnable1;
BOOL m_bEnable2;
//在CMainFrame类的机关函数中初始化成员变量
CMainFrame::CMainFrame()
{
m_bEnable1 = TRUE;
m_bEnable2 = FALSE;
}
//在CMainFrame类中为菜单项添加号令处理惩罚函数
void CMainFrame::OnTestMenu1()
{
m_bEnable1 = FALSE;
m_bEnable2 = TRUE;
}
void CMainFrame::OnUpdateTestMenu1(CCmdUI pCmdUI)
{
//启动或禁用菜单1
pCmdUI->Enable(m_bEnable1);
}
void CMainFrame::OnTestMenu2()
{
m_bEnable1 = TRUE;
m_bEnable2 = FALSE;
}
void CMainFrame::OnUpdateTestMenu2(CCmdUI pCmdUI)
{
//启动或禁用菜单2
pCmdUI->Enable(m_bEnable2);
}
27 设置菜单项的搜检状况
可以在类的 UPDATE_COMMAND_UI 消息处理惩罚函数中调用 CCmdUI::SetCheck 函数设置菜单项的搜检状况(选中/不选中状况)。
//在CMainFrame类中添加成员变量
public:
int m_nCheck;
//在CMainFrame类的机关函数中初始化成员变量
CMainFrame::CMainFrame()
{
m_nCheck = 0;
}
//在CMainFrame类中为菜单项添加号令处理惩罚函数
void CMainFrame::OnTestMenu()
{
if (m_nCheck == 0)
{
m_nCheck = 1;
}
else
{
m_nCheck = 0;
}
}
void CMainFrame::OnUpdateTestMenu(CCmdUI pCmdUI)
{
//设置菜单项搜检状况
pCmdUI->SetCheck(m_nCheck);
}
28 快捷菜单
实现快捷菜单可以在 CWnd::OnContextMenu 重载函数中调用 CMenu::TrackPopupMenu 函数。
在资料中添加1个菜单资料,ID 为 IDR_MENU。在菜单资料中添加1个子菜单,并添加菜单项。
//在CDemoView类中重载CWnd::OnContextMenu函数
void CDemoView::OnContextMenu(CWnd pWnd, CPoint point)
{
CMenu menu;
//加载菜单
if (!menu.LoadMenu(IDR_MENU))
{
return;
}
//获得子菜单
CMenu pPopupMenu = menu.GetSubMenu(0);
//弹出菜单
pPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y,pWnd);
}
void CDemoView::OnTestMenu1()
{
AfxMessageBox(_T(快捷菜单项1号令));
}
void CDemoView::OnTestMenu2()
{
AfxMessageBox(_T(快捷菜单项2号令));
}
29 获取光标的坐标
可以在 WM_MOUSEMOVE 消息处理惩罚函数中获得光标的地位。
//在CDemoView类中添加成员变量
public:
CPoint m_Point;
//在类中添加WM_MOUSEMOVE消息处理惩罚函数
void CDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
//保存光标坐标
m_Point = point;
//刷新客户区
Invalidate();
CView::OnMouseMove(nFlags, point);
}
//在类中重载CView::OnDraw函数
void CDemoView::OnDraw(CDC pDC)
{
//获得客户区坐标
CRect rect;
GetClientRect(rect);
//绘制十字光标
pDC->MoveTo(0,m_Point.y);
pDC->LineTo(rect.Width(),m_Point.y);
pDC->MoveTo(m_Point.x,0);
pDC->LineTo(m_Point.x,rect.Height());
//输出光标坐标
CString strText = _T();
strText.Format(_T(%d,%d),m_Point.x,m_Point.y);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextAlign(TA_RIGHT | TA_BOTTOM);
pDC->TextOut(m_Point.x,m_Point.y,strText);
}
30 限制光标的移动局限
限制光标的局限可以调用 SDK 的 ClipCursor 函数。
//在CDemoView类中重载CView::OnDraw函数
void CDemoView::OnDraw(CDC pDC)
{
//获得客户区坐标
CRect rect;
GetClientRect(rect);
rect.left = rect.left + rect.Width() / 4;
rect.right = rect.right - rect.Width() / 4;
rect.top = rect.top + rect.Height() / 4;
rect.bottom = rect.bottom -rect.Height() / 4;
//绘制光标移动局限
pDC->Rectangle(rect);
}
//在CDemoView类平分别添加WM_LBUTTONDOWN和WM_LBUTTONUP消息处理惩罚函数
void CDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
//获得客户区坐标
CRect rect;
GetClientRect(rect);
rect.left = rect.left + rect.Width() / 4;
rect.right = rect.right - rect.Width() / 4;
rect.top = rect.top + rect.Height() / 4;
rect.bottom = rect.bottom - rect.Height() /4;
//映射屏幕坐标
ClientToScreen(rect);
//限制光标移动局限
ClipCursor(&rect);
CView::OnLButtonDown(nFlags, point);
}
void CDemoView::OnLButtonUp(UINT nFlags, CPoint point)
{
//光标移动
ClipCursor(NULL);
CView::OnLButtonUp(nFlags, point);
}
31 自定义光标
应用自定义光标,起首调用 CWinApp::LoadCursor函数加载光标,然后调用SDK的SetCursor函数设置光标。
//创建1个单文档的应用法度,并添加2个光标资料,在CDemoView类中添加成员变量
public:
HCURSOR m_hCursor;
//在CDemoView类中重载CView::OnInitialUpdate函数
void CDemo2View::OnInitialUpdate()
{
CView::OnInitialUpdate();
//加载光标
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR1);
}
//在CDemoView类中添加WM_SETCURSOR消息处理惩罚函数
BOOL CDemo2View::OnSetCursor(CWnd pWnd, UINT nHitTest, UINT message)
{
//设置光标
::SetCursor(m_hCursor);
return TRUE;
}
//在CDemoView类平分别添加WM_LBUTTONDOWN和WM_LBUTTONUP消息处理惩罚函数
void CDemo2View::OnLButtonDown(UINT nFlags, CPoint point)
{
//加载光标
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR2);
//设置光标
::SetCursor(m_hCursor);
CView::OnLButtonDown(nFlags, point);
}
void CDemo2View::OnLButtonUp(UINT nFlags, CPoint point)
{
//加载光标
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR1);
//设置光标
::SetCursor(m_hCursor);
CView::OnLButtonUp(nFlags, point);
}
32 守候光标
启动守候光标可以调用 CCmdTarget::BeginWaitCursor函数,停止守候光标可以调用CCmdTarget::EndWaitCursor函数。
void CDemo2View::OnLButtonUp(UINT nFlags, CPoint point)
{
//启动守候光标
BeginWaitCursor();
//休眠
Sleep(5000);
//停止守候光标
EndWaitCursor();
CView::OnLButtonDown(nFlags,point);
}
真正的心灵世界会告诉你根本看不见的东西,这东西需要你付出思想和灵魂的劳动去获取,然后它会照亮你的生命,永远照亮你的生命。——王安忆《小说家的十三堂课》