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