VS2010下对话框添加菜单栏和工具栏及其启用,禁用同步

时间:2021-04-30 23:25:23

VS2010下对话框添加菜单栏和工具栏及其启用,禁用同步

在对话框模式下,菜单栏和工具栏都需要我们自己手动添加。

<一>添加菜单栏

1、菜单栏的添加比较简单,只需我们创建好“菜单”资源后(即:资源视图->右键单击”XXXDlg.rc”->”添加资源”->资源类型”Menu”->”新建”)创建我们想要的菜单。

2、双击资源视图下的对话框ID->”属性”->找到”MENU”->单击下箭头找到创建好的MENU的ID->选中保存。

运行对话框,就会看到菜单栏已在对话框的顶部了。

<二>添加工具栏

1、首先我们创建工具栏(资源类型”Toolbar”)或加载事先已经准备好的工具栏来源(资源类型”Toolbar”->导入)

2、有了工具栏,那么在XXXDlg.h中,添加CToolBar类型或其派生类型的一个变量如:

(CdlgToolBar m_ToolBar),在XXXDlg.cpp中添加OnInitDialog函数中添加如下代码(最简单但不完全):

1
2
3
4
5
6
7
8
if (!m_ToolBar.CreateEx(this, TBSTYLE_FLAT,  WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_GRIPPER | 
        CBRS_TOOLTIPS/*, CRect(0,0,0,0)*/) || !m_ToolBar.LoadToolBar(IDR_TOOLBAR2) )
{
    TRACE0("failed to create toolbar\n");
    return FALSE;
}
 
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);  // 显示工具栏

或如下代码(完整完全自己控制):

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

// Create toolbar at the top of the dialog window
if (m_ToolBar.Create(this))
{
    m_ToolBar.LoadBitmap(m_nIDBitmap);
    m_ToolBar.SetButtons(m_lpaIDToolBar, m_cIDToolBar);
}

m_ToolBar.SetBarStyle(m_ToolBar.GetBarStyle() |
    CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

// We need to resize the dialog to make room for control bars.
// First, figure out how big the control bars are.
CRect rcClientStart;
CRect rcClientNow;
GetClientRect(rcClientStart);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
    0, reposQuery, rcClientNow);

// Now move all the controls so they are in the same relative
// position within the remaining client area as they would be
// with no control bars.
CPoint ptOffset(rcClientNow.left - rcClientStart.left,
    rcClientNow.top - rcClientStart.top);

CRect  rcChild;
CWnd* pwndChild = GetWindow(GW_CHILD);
while (pwndChild)
{
    pwndChild->GetWindowRect(rcChild);
    ScreenToClient(rcChild);
    rcChild.OffsetRect(ptOffset);
    pwndChild->MoveWindow(rcChild, FALSE);
    pwndChild = pwndChild->GetNextWindow();
}

// Adjust the dialog window dimensions
CRect rcWindow;
GetWindowRect(rcWindow);
rcWindow.right += rcClientStart.Width() - rcClientNow.Width();
rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height();
MoveWindow(rcWindow, FALSE);

// And position the control bars
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);

// Set the icon for this dialog.  The framework does this automatically
//  when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);         // Set big icon
SetIcon(m_hIcon, FALSE);        // Set small icon

// Finally, center the dialog on the screen
CenterWindow();

那么,现在运行程序,就可以看到效果。

 

<三>菜单栏的状态(选中、打钩、禁用)改变

基于对话框的菜单栏的状态的改变需要我们自己实现,这里就可以借助单文档,多文档等的实现来给对话框设置对话框的状态改变。网上有这么这么一种方法,是可用的,:

问题描述:

在基于对话框的MFC应用应用程序中,自己给对话框建立一个菜单,想让菜单选中后,对应的菜单项前显示“对勾”或“点”,

但无法用ON_UPDATE_COMMAND_UI更新菜单。

从命令用户界面处理函数(Command UI handler)改变菜单状态(启用/禁用,选择/取消选择,更改文字)在由对话框处理时没有正常工作。

void CTestDlg::OnUpdateFileExit(CCmdUI*pCmdUI)

{

   pCmdUI->SetCheck(TRUE); // 没有文字前显示选定标记.

   pCmdUI->SetRadio(TRUE); // 没有在文字前显示点.

   pCmdUI->SetText("Close"); //没有更改菜单文字.

}

 

解决如下:

原因

下拉菜单显示的时候, WM_INITMENUPOPUP消息被先发送以显示菜单项MFC CFrameWnd::OnInitMenuPopup函数遍历菜单项并为每个菜单项调用

更新命令处理函数(如果有的话).菜单的外观被更新以反映它的状态(启用/禁用,选择/取消选择)

更新用户界面机制在基于对话框的应用程序中不能工作,因为CDialog没有OnInitMenuPopup处理函数,而使用CWnd's默认处理函数,该函数没有为菜单项调用更新命令处理函数。

添加该处理函数,可以使用类向导来添加(即:项目->类向导->类名选”CXXXDlg”->消息里搜” WM_INITMENUPOPUP”->添加)如果消息里没有该消息,可以使用类向导类的”添加自定义消息”进行添加或进行手动添加。如: 

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
//在CXXXDlg.h中
Public:
//......
afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); // 添加
//......
DECLARE_MESSAGE_MAP()
//在CXXXDlg.cpp中
BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
    //......
    ON_WM_INITMENUPOPUP()  // 添加
    //......
END_MESSAGE_MAP()

void CXXXDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
    CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);

    if(!bSysMenu && pPopupMenu)
    {
        CCmdUI cmdUI;
        cmdUI.m_pOther = NULL;
        cmdUI.m_pMenu = pPopupMenu;
        cmdUI.m_pSubMenu = NULL;

        UINT count = pPopupMenu->GetMenuItemCount();
        cmdUI.m_nIndexMax = count;
        for(UINT i=0; i<count; i++)
        {
            UINT nID = pPopupMenu->GetMenuItemID(i);
            if(-1 == nID || 0 == nID)
            {
                continue;
            }
            cmdUI.m_nID = nID;
            cmdUI.m_nIndex = i;
            cmdUI.DoUpdate(this, FALSE);
        }
    } 
}

设置完上述步骤之之后,再为各个菜单项的ID分别添加ON_UPDATE_COMMAND_UI菜单命令更新机制。其中一个如:

(项目->类向导->类名选”CXXXDlg”->命令”菜单项ID”->”UPDATE_COMMAND_UI”->编辑代码) 

1
2
3
4
5
void CToolBarDlgDlg::OnUpdateDelete(CCmdUI *pCmdUI)
{
    // TODO: 在此添加命令更新用户界面处理程序代码
    pCmdUI->Enable(TRUE);
}

在VS2010下,我开始依照MSDN的DLGcbr32例子(后面有这个例子的链接地址)来修改, 从CTooBar类中派生出一个控制条来CDlgToolBar类中,在此类中添加响应 WM_IDLEUPDATECMDUI 消息(参照DLGcbr32代码里的Dlgbars.h和Dlgbars.cpp)设置完上述之后,在菜单栏里就可以看到设置效果。但这时,工具栏和菜单栏并不能够做到同步,工具栏的设置还需要要一些努力。

但我的Dlg类始终不能够响应WM_IDLEUPDATECMDUI这个消息,可以在类向导中重载WindowProc函数,看能否捕捉到WM_IDLEUPDATECMDUI,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LRESULT CToolBarDlgDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: 在此添加专用代码和/或调用基类
    if(message == WM_IDLEUPDATECMDUI)
    {
        AfxMessageBox(_T("响应了WM_IDLEUPDATECMDUI消息"));
        return 0;
    }
//  if (message == WM_KICKIDLE)
//  {
//      //AfxMessageBox(_T("响应了WM_KICKIDLE消息"));
//      //SendMessage(WM_IDLEUPDATECMDUI, wParam, lParam);
//      UpdateDialogControls(this, true); 
//      return 0;
//  }

    return CDialog::WindowProc(message, wParam, lParam);
}

通过测试,我的对话框是捕捉不到WM_IDLEUPDATECMDUI消息的,这就导致了不能在CdlgToolBar类响应WM_IDLEUPDATECMDUI自然对话框中的工具栏各选项得不到应答。所以,还好,

<四>菜单栏,工具栏(禁用,启用)同步

下面方法就不需要响应WM_IDLEUPDATECMDUI、WM_INITMENUPOPUP消息了,就可以实现菜单栏,工具栏同步。

(1)虚函数Cdialog::ContinueModal函数可以实现类似功能,重载此函数代码如下:  

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
BOOL CToolBarDlgDlg::ContinueModal()
{
    // TODO: 在此添加专用代码和/或调用基类
    if(m_ToolBar.IsWindowVisible())  // 如果对话框有工具栏,m_ToolBar为工具栏 
    {
        CFrameWnd* pParent = (CFrameWnd*) m_ToolBar.GetParent();
        if(pParent)
            m_ToolBar.OnUpdateCmdUI(pParent, (WPARAM)TRUE);
    }

    CMenu* pMainMenu = GetMenu();    // 如果对话框存在菜单,更新菜单 
    CCmdUI cmdUI;
    for (UINT n = 0; n < pMainMenu->GetMenuItemCount(); ++n)
    {
        CMenu* pSubMenu = pMainMenu->GetSubMenu(n);
        cmdUI.m_nIndexMax = pSubMenu->GetMenuItemCount();
        for (UINT i = 0; i < cmdUI.m_nIndexMax;++i)
        {
            cmdUI.m_nIndex = i;
            cmdUI.m_nID = pSubMenu->GetMenuItemID(i);
            cmdUI.m_pMenu = pSubMenu;
            cmdUI.DoUpdate(this, FALSE);
        }
    }

    return CDialog::ContinueModal();
}

好了,重载了此函数之后,终于实现了工具栏与菜单栏的同步问题。

(2)还有一种实现方法就是实现WM_KICKIDLE消息消息,在WM_KICKIDLE消息的映射函数中我们可以这样写(同ContinueModal函数的实现),同样可以ContinueModal函数的功能

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
afx_msg LRESULT CToolBarDlgDlg::OnKickidle(WPARAM wParam, LPARAM lParam)
{
     // 如果对话框有工具栏,m_ToolBar为工具栏 
     if(m_ToolBar.IsWindowVisible())
    {
        CFrameWnd* pParent = (CFrameWnd*) m_ToolBar.GetParent();
         if(pParent)
            m_ToolBar.OnUpdateCmdUI(pParent, (WPARAM)TRUE);
    }
 
     // 如果对话框存在菜单,更新菜单 
    CMenu* pMainMenu = GetMenu();
    CCmdUI cmdUI;
     for (UINT n =  0; n < pMainMenu->GetMenuItemCount(); ++n)
    {
        CMenu* pSubMenu = pMainMenu->GetSubMenu(n);
        cmdUI.m_nIndexMax = pSubMenu->GetMenuItemCount();
         for (UINT i =  0; i < cmdUI.m_nIndexMax;++i)
        {
            cmdUI.m_nIndex = i;
            cmdUI.m_nID = pSubMenu->GetMenuItemID(i);
            cmdUI.m_pMenu = pSubMenu;
            cmdUI.DoUpdate( this, FALSE);
        }
    }

     //UpdateDialogControls(this, true); 

     return  0;
}

更多细节详见MSDN的DLGcbr32例子:

示例介绍:http://msdn.microsoft.com/zh-cn/library/ccstww6w(v=vs.90).aspx

DlgCbr32代码:http://download.csdn.net/detail/johnnyhu90/5835135

我的测试代码:http://download.csdn.net/detail/johnnyhu90/5837561