MFC建立的MDI程式 新建一個子文件 主視窗標題自動會被追加上子文件的名字 取消此功能
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
CMDIFrameWnd::OnUpdateFrameTitle(bAddToTitle);
::SetWindowText(m_hWnd,m_strTitle);
}
現在很多應用程式的介面基本是用配置檔案來規劃介面的,在這個時候就得學會自定義選單欄和工具欄之類的。
VS Feature Pack是為微軟新推出的介面庫(聽說是買BCG的授權,然後對之進行改造的),其中的主要的介面類可以和BCG的介面類可以對應起來,類的使用和BCG的也大同小異。但是有些做法還是很不一樣,比如這次我要提到的自定義選單欄。這裡的自定義選單欄是指去除系統預設的選單欄,然後動態建立選單欄。今天摸索了一下,大致搞清楚了(說實話,這方面網上的資料很少)。
首先我們新建一個MFC的單文件工程:DynamicMenu,基本設定如下:
這裡要提一下的是VS Feature Pack的應用程式其中的選單欄操作主要由CMFCMenuBar來負責。因此下面的編碼也主要針對該類來進行。
首先我們實現編碼實現刪除預設的所有系統選單項,其程式碼如下:
view plaincopy to clipboardprint?
// 刪除預設的所有系統選單項
static void DelAllMenu(HMENU hMenu)
{
int Menucount = ::GetMenuItemCount(hMenu);
for (int i = Menucount-1;i>-1;i--)
{
::DeleteMenu(hMenu,i, MF_BYPOSITION);
}
}
// 刪除預設的所有系統選單項
static void DelAllMenu(HMENU hMenu)
{
int Menucount = ::GetMenuItemCount(hMenu);
for (int i = Menucount-1;i>-1;i--)
{
::DeleteMenu(hMenu,i, MF_BYPOSITION);
}
}
然後我們定義兩個選單資源ID:
view plaincopy to clipboardprint?
#define ID_NEW_MENUBAR_OPEN 5000
#define ID_NEW_MENUBAR_SAVE 5001
#define ID_NEW_MENUBAR_OPEN 5000
#define ID_NEW_MENUBAR_SAVE 5001
為CMainFrame類新增一個建立選單欄的成員函式:
view plaincopy to clipboardprint?
void CMainFrame::NewMenuBar()
{
CMenu menu;
menu.CreateMenu();
CString strMenu;
strMenu = _T("開啟檔案");
menu.AppendMenu(MF_ENABLED|MF_STRING,ID_NEW_MENUBAR_OPEN,strMenu);
strMenu = _T("儲存檔案");
menu.AppendMenu(MF_ENABLED|MF_STRING,ID_NEW_MENUBAR_SAVE,strMenu);
CString strMenuBarTitle;
strMenuBarTitle = _T("檔案");;
m_wndMenuBar.InsertButton (CMFCToolBarMenuButton (0, menu, -1,strMenuBarTitle));
}
void CMainFrame::NewMenuBar()
{
CMenu menu;
menu.CreateMenu();
CString strMenu;
strMenu = _T("開啟檔案");
menu.AppendMenu(MF_ENABLED|MF_STRING,ID_NEW_MENUBAR_OPEN,strMenu);
strMenu = _T("儲存檔案");
menu.AppendMenu(MF_ENABLED|MF_STRING,ID_NEW_MENUBAR_SAVE,strMenu);
CString strMenuBarTitle;
strMenuBarTitle = _T("檔案");;
m_wndMenuBar.InsertButton (CMFCToolBarMenuButton (0, menu, -1,strMenuBarTitle));
}
我們在CMainFrame類的OnCreate函式呼叫這個函式,這裡只給出部分程式碼:
view plaincopy to clipboardprint?
if (!m_wndMenuBar.Create(this))
{
TRACE0("Failed to create menubar\n");
return -1; // fail to create
}
m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
HMENU hm = m_wndMenuBar.GetDefaultMenu();
// 刪除預設選單欄
if (NULL!=hm)
{
DelAllMenu(hm);
}
// 建立新的選單欄
NewMenuBar();
if (!m_wndMenuBar.Create(this))
{
TRACE0("Failed to create menubar\n");
return -1; // fail to create
}
m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
HMENU hm = m_wndMenuBar.GetDefaultMenu();
// 刪除預設選單欄
if (NULL!=hm)
{
DelAllMenu(hm);
}
// 建立新的選單欄
NewMenuBar();
現在我們看看效果如何,如下圖:
我們發現預設選單欄去掉了,但是新的選單欄並沒有出來。到網上搜資料,但是並沒有搜到適用的,看了看Visual C++ 2008 Feature Pack Demo中提供的DynamicMenu的原始碼,瞭解了要增加AFX_WM_RESETMENU訊息的處理函式,在函式裡呼叫建立選單欄,具體增加的程式碼如下:
view plaincopy to clipboardprint?
// MainFrm.h : interface of the CMainFrame classafx_msg
// AFX_WM_RESETMENU訊息的處理函式宣告
LRESULT OnResetMenu(WPARAM,LPARAM);
// MainFrm.cpp : implementation of the CMainFrame class
// 訊息巨集中增加
ON_REGISTERED_MESSAGE(AFX_WM_RESETMENU,&CMainFrame::OnResetMenu)
LRESULT CMainFrame::OnResetMenu(WPARAM,LPARAM)
{
NewMenuBar();
return 0;
}
// MainFrm.h : interface of the CMainFrame classafx_msg
// AFX_WM_RESETMENU訊息的處理函式宣告
LRESULT OnResetMenu(WPARAM,LPARAM);
// MainFrm.cpp : implementation of the CMainFrame class
// 訊息巨集中增加
ON_REGISTERED_MESSAGE(AFX_WM_RESETMENU,&CMainFrame::OnResetMenu)
LRESULT CMainFrame::OnResetMenu(WPARAM,LPARAM)
{
NewMenuBar();
return 0;
}
我們再刪除程式的登錄檔相關項重新編譯(使用VS Feature Pack開發刪除登錄檔這一項非常重要,Feature Pack的介面設計儲存思路實際上和BCG是一樣的,把上次使用者設定的介面配置資訊都儲存在登錄檔,如果不刪除登錄檔相關項,往往不能更新介面,登錄檔相關項一般在HKEY_CURRENT_USER\Software\Local AppWizard-Generated Applications\你的工程名稱(英文版VS),HKEY_CURRENT_USER\Software\應用程式嚮導生成的本地應用程式\你的工程名稱(中文版VS))。
我們再看看效果,如下圖:
你可能會發現選單是灰的,那是沒有新增選單的命令響應函式的緣故。本文的編譯環境為:Windows XP + sp3, VS C++ 2008 + sp1。
後來查了一下MSDN對AFX_WM_RESETMENU訊息的解釋,如下:
參考文獻:
1. AFX Messages
在VC6.0和VS2010裡面動態新增選單項是不一樣的,檢視MSDN文件可知,VS2010採用的是MFC9.0版,其中有很多新增的項具體資訊請檢視http://msdn.microsoft.com/en-us/library/ws8s10w4.aspx,本文就根據自己的測試詳細的比較一下二者的區別:
1.在VC6.0裡面動態新增一個子選單項:
在CMainFrame::OnCtreate()中新增程式碼,另外要在Resource.h裡面新增#define ID_MENU_ADDMENUITEM 32773
CMainFrame::OnCtreate(){
//下面是新增的程式碼
CMenu *pMenu=AfxGetMainWnd()->GetMenu();
CMenu *pmSub=pMenu->GetSubMenu(1);
pmSub->AppendMenu(MF_STRING,ID_MENU_ADDMENUITEM,L"Add Menu &Item");
}//效果是在“Edit”選單最下面添加了一個"Add Menu Item"子項
2.在VS2010裡面新增一個子選單項:
要對CMainFrame類的OnShowPopupMenu()進行過載,另外要在Resource.h裡面新增#define IDS_EDIT_MYITEM_1 32773
BOOL CMainFrame::OnShowPopupMenu(CMFCPopupMenu* pMenuPopup)
{
// TODO: Add your specialized code here and/or call the base class
int iIndex = -1;
if (!CMFCToolBar::IsCustomizeMode()&&(iIndex=pMenuPopup->GetMenuBar()->CommandToIndex(ID_EDIT_PASTE))>=0)
{
pMenuPopup->InsertSeparator(iIndex+1);
pMenuPopup->InsertItem(CMFCToolBarMenuButton(IDS_EDIT_MYITEM_1,NULL,-1,_T("&MyItem 1")),iIndex+2);
} //使用CommandToIndex()來獲得選單項的索引,然後根據索引來確定子選單項的新增位置
return CFrameWndEx::OnShowPopupMenu(pMenuPopup);
}//效果是在“Edit”選單最下面添加了一個分割線和一個"MyItem 1"子項
效果如圖,因為還沒有為其新增處理函式,所以呈灰色:
給新增的子選單項新增訊息處理函式:
在MainFrame.h裡面新增訊息處理函式宣告:
class CMainFrame : public CFrameWnd{
//…
protected:
afx_msg void OnEditMyItem1 ();
}
然後在MainFrame.cpp訊息對映裡面新增訊息對映項:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//…
ON_COMMAND(IDS_EDIT_MYITEM_1, OnEditMyItem1)
END_MESSAGE_MAP()
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CMFCMenuBar的繼承關係:
CObject
CCmdTarget
CWnd
CBasePane
CPane
CMFCBaseToolBar
CMFCToolBar
CMFCMenuBar
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++