精通 VC++ 实效编程280例 - 02 菜单和光标

菜单和关闭时重要的 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:删除菜单项,如果菜单项与弹出菜单相关联,将销毁弹出菜单的句柄,并释放其占用的内存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#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 重载函数中处理菜单命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//在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 & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else if(nID == ID_TEST_MENU)
    {
        AfxMessageBox(_T("测试菜单项命令"));
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

25 禁用关闭按钮

禁用关闭按钮可以调用 CMenu::EnableMenuItem 函数。

1
2
3
4
5
6
7
8
9
10
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 函数,启用和禁用菜单项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//在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 函数设置菜单项的检查状态(选中/不选中状态)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//在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个子菜单,并添加菜单项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//在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 消息处理函数中获得光标的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//在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 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//在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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//创建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函数。

1
2
3
4
5
6
7
8
9
10
void CDemo2View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //启动等待光标
    BeginWaitCursor();
    //休眠
    Sleep(5000);
    //结束等待光标
    EndWaitCursor();
    CView::OnLButtonDown(nFlags,point);
}

转自:http://www.cnblogs.com/iwanc/archive/2013/06/09/2987866.html 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。