==================================聲明==================================
本文原創,轉載在正文中顯要的注明作者和出處,並保證文章的完整性。
未經作者同意請勿修改(包括本聲明),保留法律追究的權利。
未經作者同意請勿用於出版、印刷或學術引用。
本文不定期修正完善,為保證內容正確,建議移步原文處閱讀。
本文鏈接:http://www.cnblogs.com/wlsandwho/p/4282242.html
=======================================================================
水平太低了,在青島找個稱心的工作,難——真的是不開空調啊,獵頭怎么不信我呢!(這個梗請點我點我)
這么晚了,還是寫個博客看會兒書再睡覺吧,人丑就要多……
=======================================================================
先創建一個MFC程序。



工程創建完后,編譯運行一下,看看默認是什么樣子。如下圖。

點擊窗口-新建窗口(N),效果如圖:

可以看到,兩個窗口都是一樣的。
=======================================================================
下面,就要開始魔改了。
=======================================================================
添加一個對話框資源,ID命名為IDD_FORMVIEW1。


給剛添加的資源添加類,CFV1,其基類為CFormView。

添加對IDC_BUTTON1的消息處理函數。
1 void CFV1::OnBnClickedButton1() 2 { 3 // TODO: 在此添加控件通知處理程序代碼 4 MessageBox(TEXT("1"),TEXT("CFV1"),MB_OK); 5 }
同樣的步驟,再添加CFV2和對IDD_FORMVIEW2中IDC_BUTTON1的消息處理函數。
1 void CFV2::OnBnClickedButton1() 2 { 3 // TODO: 在此添加控件通知處理程序代碼 4 MessageBox(TEXT("2"),TEXT("CFV2"),MB_OK); 5 }
在TestMDIWLS.h中添加頭文件
1 #include "FV1.h" 2 #include "FV2.h"
=======================================================================
到這里,讓我們暫停一下。下面要進入重點了。
=======================================================================
對模板的注冊是在CTestMDIWLSApp::InitInstance中實現的,下面是MFC的MDI中原有的代碼。
1 // CTestMDIWLSApp 初始化 2 3 BOOL CTestMDIWLSApp::InitInstance() 4 { 5 // 如果一個運行在 Windows XP 上的應用程序清單指定要 6 // 使用 ComCtl32.dll 版本 6 或更高版本來啟用可視化方式, 7 //則需要 InitCommonControlsEx()。否則,將無法創建窗口。 8 INITCOMMONCONTROLSEX InitCtrls; 9 InitCtrls.dwSize = sizeof(InitCtrls); 10 // 將它設置為包括所有要在應用程序中使用的 11 // 公共控件類。 12 InitCtrls.dwICC = ICC_WIN95_CLASSES; 13 InitCommonControlsEx(&InitCtrls); 14 15 CWinAppEx::InitInstance(); 16 17 18 // 初始化 OLE 庫 19 if (!AfxOleInit()) 20 { 21 AfxMessageBox(IDP_OLE_INIT_FAILED); 22 return FALSE; 23 } 24 25 AfxEnableControlContainer(); 26 27 EnableTaskbarInteraction(); 28 29 // 使用 RichEdit 控件需要 AfxInitRichEdit2() 30 // AfxInitRichEdit2(); 31 32 // 標准初始化 33 // 如果未使用這些功能並希望減小 34 // 最終可執行文件的大小,則應移除下列 35 // 不需要的特定初始化例程 36 // 更改用於存儲設置的注冊表項 37 // TODO: 應適當修改該字符串, 38 // 例如修改為公司或組織名 39 SetRegistryKey(_T("應用程序向導生成的本地應用程序")); 40 LoadStdProfileSettings(0); // 加載標准 INI 文件選項(包括 MRU) 41 42 43 InitContextMenuManager(); 44 45 InitKeyboardManager(); 46 47 InitTooltipManager(); 48 CMFCToolTipInfo ttParams; 49 ttParams.m_bVislManagerTheme = TRUE; 50 theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL, 51 RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams); 52 53 // 注冊應用程序的文檔模板。文檔模板 54 // 將用作文檔、框架窗口和視圖之間的連接 55 CMultiDocTemplate* pDocTemplate; 56 pDocTemplate = new CMultiDocTemplate(IDR_TestMDIWLSTYPE, 57 RUNTIME_CLASS(CTestMDIWLSDoc), 58 RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架 59 RUNTIME_CLASS(CTestMDIWLSView)); 60 if (!pDocTemplate) 61 return FALSE; 62 AddDocTemplate(pDocTemplate); 63 64 // 創建主 MDI 框架窗口 65 CMainFrame* pMainFrame = new CMainFrame; 66 if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME)) 67 { 68 delete pMainFrame; 69 return FALSE; 70 } 71 m_pMainWnd = pMainFrame; 72 // 僅當具有后綴時才調用 DragAcceptFiles 73 // 在 MDI 應用程序中,這應在設置 m_pMainWnd 之后立即發生 74 75 // 分析標准 shell 命令、DDE、打開文件操作的命令行 76 CCommandLineInfo cmdInfo; 77 ParseCommandLine(cmdInfo); 78 79 80 81 // 調度在命令行中指定的命令。如果 82 // 用 /RegServer、/Register、/Unregserver 或 /Unregister 啟動應用程序,則返回 FALSE。 83 if (!ProcessShellCommand(cmdInfo)) 84 return FALSE; 85 // 主窗口已初始化,因此顯示它並對其進行更新 86 pMainFrame->ShowWindow(m_nCmdShow); 87 pMainFrame->UpdateWindow(); 88 89 return TRUE; 90 }
注意這一塊代碼:(我手工對齊了一下)
1 // 注冊應用程序的文檔模板。文檔模板 2 // 將用作文檔、框架窗口和視圖之間的連接 3 CMultiDocTemplate* pDocTemplate; 4 pDocTemplate = new CMultiDocTemplate(IDR_TestMDIWLSTYPE, 5 RUNTIME_CLASS(CTestMDIWLSDoc), 6 RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架 7 RUNTIME_CLASS(CTestMDIWLSView)); 8 if (!pDocTemplate) 9 return FALSE; 10 AddDocTemplate(pDocTemplate);
這里我們需要做的就是new一個自己新建的CFV,然后把它加入文檔模板。
所以先再分析下CMultiDocTemplate(...)函數
1 CMultiDocTemplate( 2 UINT nIDResource, 3 CRuntimeClass* pDocClass, 4 CRuntimeClass* pFrameClass, 5 CRuntimeClass* pViewClass 6 );
后三個參數一目了然,形參自注釋。
要注意的是第一個參數:UINT nIDResource
MSDN中這樣說的:
nIDResource Specifies the ID of the resources used with the document type. This may include menu, icon, accelerator table, and string resources. The string resource consists of up to seven substrings separated by the '\n' character (the '\n' character is needed as a place holder if a substring is not included; however, trailing '\n' characters are not necessary); these substrings describe the document type. For information on the substrings, see CDocTemplate::GetDocString. This string resource is found in the application's resource file. For example: // MYCALC.RC STRINGTABLE PRELOAD DISCARDABLE BEGIN IDR_SHEETTYPE "\nSheet\nWorksheet\nWorksheets (*.myc)\n.myc\n MyCalcSheet\nMyCalc Worksheet" END Note that the string begins with a '\n' character; this is because the first substring is not used for MDI applications and so is not included. You can edit this string using the string editor; the entire string appears as a single entry in the String Editor, not as seven separate entries. For more information about these resource types, see Resource Editors.
在這里最簡單的方式就是在IDE里查找原來的IDR_TestMDIWLSTYPE所對應的字符串,然后模仿着修改。
我嘗試了下直接用Sting_Table添加,並修改了一個字符串的部分字段(AAAA和BBBB),另一個未變,以便觀察效果。(大晚上的我要是願意看英文我早就去看美劇了。)

下面是在rc文件中的編碼。(新手不要手工編輯,相關資料參見MSDN和羅雲彬的匯編書,PS:典藏版亞馬遜有售哦!)
1 ///////////////////////////////////////////////////////////////////////////// 2 // 3 // String Table 4 // 5 6 STRINGTABLE 7 BEGIN 8 IDP_OLE_INIT_FAILED "OLE 初始化失敗。請確保 OLE 庫是正確的版本。" 9 IDR_CFV1_TYPE "\nAAAA\nBBBB\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 10 IDR_CFV2_TYPE "\nTestMDIWLS\nTestMDIWLS\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 11 END 12 13 STRINGTABLE 14 BEGIN 15 IDR_MAINFRAME "TestMDIWLS" 16 IDR_TestMDIWLSTYPE "\nTestMDIWLS\nTestMDIWLS\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 17 ID_WINDOW_MANAGER "窗口(&W)..." 18 END 19 20 STRINGTABLE 21 BEGIN 22 AFX_IDS_APP_TITLE "TestMDIWLS" 23 AFX_IDS_IDLEMESSAGE "就緒" 24 END
可以看到13-18行是MFC自帶的,我們手工添加的是7-11中的9、10兩行。
根本就沒有MSDN上面說的那個PRELOAD DISCARDABLE。看來不預加載也沒問題啊。當然這是猜測,可能是資源太小了。
=======================================================================
看完剛才的資料,可以嘗試着添加文檔模板了。當然要是有閑心情——現在是深夜,不管你有沒有,我反正沒有——可以看下
https://msdn.microsoft.com/zh-cn/library/2b4xctyw%28v=vs.100%29.aspx和https://msdn.microsoft.com/zh-cn/library/feh4ww6k%28v=vs.100%29.aspx
=======================================================================
現在我們添加模板文檔。先添加一個試試。
1 // 注冊應用程序的文檔模板。文檔模板 2 // 將用作文檔、框架窗口和視圖之間的連接 3 CMultiDocTemplate* pDocTemplate; 4 pDocTemplate = new CMultiDocTemplate(IDR_TestMDIWLSTYPE, 5 RUNTIME_CLASS(CTestMDIWLSDoc), 6 RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架 7 RUNTIME_CLASS(CTestMDIWLSView)); 8 if (!pDocTemplate) 9 return FALSE; 10 AddDocTemplate(pDocTemplate); 11 12 ////////////////////////////////////////////////////////////////////////// 13 //by wls 20150209 22:17:50 14 pDocTemplate = new CMultiDocTemplate(IDR_CFV1_TYPE, 15 RUNTIME_CLASS(CTestMDIWLSDoc), 16 RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架 17 RUNTIME_CLASS(CFV1)); 18 if (!pDocTemplate) 19 return FALSE; 20 AddDocTemplate(pDocTemplate); 21 22 //////////////////////////////////////////////////////////////////////////
編譯、運行,效果:

注意這里是TestMDIWLS和BBBB。
點擊BBBB確定后:

這里是AAAA1。
點擊按鈕1后:

現在就可以更改String_Table里的東西了。我手工改,畢竟編輯器有點蹩腳。
1 ///////////////////////////////////////////////////////////////////////////// 2 // 3 // String Table 4 // 5 6 STRINGTABLE 7 BEGIN 8 IDP_OLE_INIT_FAILED "OLE 初始化失敗。請確保 OLE 庫是正確的版本。" 9 IDR_CFV1_TYPE "\nCFV1的FormView視圖\nCFV1\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 10 IDR_CFV2_TYPE "\nCFV2的FormView視圖\nCFV2\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 11 END
再添加上CFV2:
1 ////////////////////////////////////////////////////////////////////////// 2 //by wls 20150209 22:17:50 3 pDocTemplate = new CMultiDocTemplate(IDR_CFV1_TYPE, 4 RUNTIME_CLASS(CTestMDIWLSDoc), 5 RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架 6 RUNTIME_CLASS(CFV1)); 7 if (!pDocTemplate) 8 return FALSE; 9 AddDocTemplate(pDocTemplate); 10 11 pDocTemplate = new CMultiDocTemplate(IDR_CFV2_TYPE, 12 RUNTIME_CLASS(CTestMDIWLSDoc), 13 RUNTIME_CLASS(CChildFrame), // 自定義 MDI 子框架 14 RUNTIME_CLASS(CFV2)); 15 if (!pDocTemplate) 16 return FALSE; 17 AddDocTemplate(pDocTemplate); 18 19 //////////////////////////////////////////////////////////////////////////
編譯運行:

點擊CFV1后確定:

點擊新建,點擊CFV2后確定:

發現確實是創建了2個模板。
=======================================================================
現在問題來了,如何讓程序一開始運行時,就打開了2個模板?畢竟讓用戶自己手工點擊,99.999%是會有問題的。
=======================================================================
讓我們來回憶一下,我們是如何創建模板窗口的?第一個是程序默認給出的選擇框。沒錯。
那第二個呢?第二個是我們手工點擊的新建!而且出來的是一模一樣的選擇框!
現在我們要做的就是先看看這個點擊新建的響應函數是怎樣的。
=======================================================================
下面是MDI中默認的響應函數的映射宏:
1 // CTestMDIWLSApp 2 3 BEGIN_MESSAGE_MAP(CTestMDIWLSApp, CWinAppEx) 4 ON_COMMAND(ID_APP_ABOUT, &CTestMDIWLSApp::OnAppAbout) 5 // 基於文件的標准文檔命令 6 ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew) 7 ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen) 8 // 標准打印設置命令 9 ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinAppEx::OnFilePrintSetup) 10 END_MESSAGE_MAP()
可以看到第6行:
1 ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew)
使用的是CWinAppEx的OnFileNew函數。顯然MFC框架的代碼是最好不修改的,只能自己模仿CWinAppEx的OnFileNew函數寫一個自己的實現,姑且叫OnFileNewWLS吧。
在動手前,先看看OnFileNew是如何實現的:
1 ///////////////////////////////////////////////////////////////////////////// 2 // WinApp features for new and open 3 4 void CWinApp::OnFileNew() 5 { 6 if (m_pDocManager != NULL) 7 m_pDocManager->OnFileNew(); 8 }
那CDocManager的實現呢?如下:
1 void CDocManager::OnFileNew() 2 { 3 if (m_templateList.IsEmpty()) 4 { 5 TRACE(traceAppMsg, 0, "Error: no document templates registered with CWinApp.\n"); 6 AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC); 7 return; 8 } 9 10 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead(); 11 if (m_templateList.GetCount() > 1) 12 { 13 // more than one document template to choose from 14 // bring up dialog prompting user 15 CNewTypeDlg dlg(&m_templateList); 16 INT_PTR nID = dlg.DoModal(); 17 if (nID == IDOK) 18 pTemplate = dlg.m_pSelectedTemplate; 19 else 20 return; // none - cancel operation 21 } 22 23 ASSERT(pTemplate != NULL); 24 ASSERT_KINDOF(CDocTemplate, pTemplate); 25 26 pTemplate->OpenDocumentFile(NULL); 27 // if returns NULL, the user has already been alerted 28 }
看完了這些,我們就知道如何實現自己的OnFileNewWLS了,這是我的代碼,僅供參考。
1 void CTestMDIWLSApp::OnFileNewWLS() 2 { 3 CDocTemplate* pTemplate = NULL; 4 5 POSITION pos = GetFirstDocTemplatePosition(); 6 while(pos) 7 { 8 pTemplate = GetNextDocTemplate(pos); 9 10 ASSERT(pTemplate != NULL); 11 ASSERT_KINDOF(CDocTemplate, pTemplate); 12 13 pTemplate->OpenDocumentFile(NULL); 14 } 15 }
不要忘了修改映射宏。
1 // CTestMDIWLSApp 2 3 BEGIN_MESSAGE_MAP(CTestMDIWLSApp, CWinAppEx) 4 ON_COMMAND(ID_APP_ABOUT, &CTestMDIWLSApp::OnAppAbout) 5 // 基於文件的標准文檔命令 6 //ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew) 7 ON_COMMAND(ID_FILE_NEW, &CTestMDIWLSApp::OnFileNewWLS) 8 ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen) 9 // 標准打印設置命令 10 ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinAppEx::OnFilePrintSetup) 11 END_MESSAGE_MAP()
編譯鏈接看效果:

注意到三個模板都實現了,但是界面有卡頓。
是時候表演真正的記性了!
手工修改String_Table。拆分第一個。添加PRELOAD DISCARDABLE。
1 STRINGTABLE 2 BEGIN 3 IDP_OLE_INIT_FAILED "OLE 初始化失敗。請確保 OLE 庫是正確的版本。" 4 END 5 6 STRINGTABLE PRELOAD DISCARDABLE 7 BEGIN 8 IDR_TestMDIWLSTYPE "\nTestMDIWLS\nTestMDIWLS\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 9 IDR_CFV1_TYPE "\nCFV1的FormView視圖\nCFV1\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 10 IDR_CFV2_TYPE "\nCFV2的FormView視圖\nCFV2\n\n\nTestMDIWLS.Document\nTestMDIWLS.Document" 11 END 12 13 STRINGTABLE 14 BEGIN 15 IDR_MAINFRAME "TestMDIWLS" 16 ID_WINDOW_MANAGER "窗口(&W)..." 17 END
可能是心理效果,我感覺略微快了那么一點點。(處女座請自重。)
=======================================================================
下面屏蔽各種新建按鈕,防止創建過多的模板視圖。
更改之前的OnFileNewWLS代碼,利用局部Static變量。
1 void CTestMDIWLSApp::OnFileNewWLS() 2 { 3 static BOOL bNew=FALSE; 4 5 if (bNew==FALSE) 6 { 7 bNew=TRUE; 8 9 CDocTemplate* pTemplate = NULL; 10 11 POSITION pos = GetFirstDocTemplatePosition(); 12 while(pos) 13 { 14 pTemplate = GetNextDocTemplate(pos); 15 16 ASSERT(pTemplate != NULL); 17 ASSERT_KINDOF(CDocTemplate, pTemplate); 18 19 pTemplate->OpenDocumentFile(NULL); 20 } 21 } 22 }
下面要屏蔽新建窗口按鈕

在rc文件中有這么一行
1 ID_WINDOW_NEW "為活動文檔打開另一個窗口\n新建窗口"
所以需要屏蔽的就是對ID_WINDOW_NEW的響應,所以添加第10行代碼,重寫自己的響應函數。
1 // CMainFrame 2 3 IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWndEx) 4 5 BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWndEx) 6 ON_WM_CREATE() 7 ON_COMMAND(ID_WINDOW_MANAGER, &CMainFrame::OnWindowManager) 8 ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnApplicationLook) 9 ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnUpdateApplicationLook) 10 ON_COMMAND(ID_WINDOW_NEW,&CMainFrame::OnWindowNewWLS) 11 END_MESSAGE_MAP()
OnWindowsWLS的實現:
1 void CMainFrame::OnWindowNewWLS() 2 { 3 //no op 4 }
現在已經能夠在運行時一次性創建3個視圖,並且不再創建額外的視圖。
=======================================================================
既然能且只能創建3個視圖,那么就不需要在選項卡上顯示表示實例數目的阿拉伯字母了。
1 BOOL CTestMDIWLSDoc::OnNewDocument() 2 { 3 if (!CDocument::OnNewDocument()) 4 return FALSE; 5 6 // TODO: 在此添加重新初始化代碼 7 // (SDI 文檔將重用該文檔) 8 CString strTitle=GetTitle(); 9 strTitle=strTitle.Left(strTitle.GetLength()-1); 10 SetTitle(strTitle); 11 12 return TRUE; 13 }
效果:

=======================================================================
下面要做的就是屏蔽彩色選項卡的關閉按鈕。
那么這個一直被我稱為選項卡/標簽頁的東西到底是什么呢?使用Spy++查看下。

顯示的是TabWnd,那么是一個類似Tab窗口的東西。這些都在CMainFrame::OnCreate里有初始化設置。
先看看MDI原有的代碼:
1 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 2 { 3 if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1) 4 return -1; 5 6 BOOL bNameValid; 7 // 基於持久值設置視覺管理器和樣式 8 OnApplicationLook(theApp.m_nAppLook); 9 10 CMDITabInfo mdiTabParams; 11 mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // 其他可用樣式... 12 mdiTabParams.m_bActiveTabCloseButton = TRUE; // 設置為 FALSE 會將關閉按鈕放置在選項卡區域的右側 13 mdiTabParams.m_bTabIcons = FALSE; // 設置為 TRUE 將在 MDI 選項卡上啟用文檔圖標 14 mdiTabParams.m_bAutoColor = TRUE; // 設置為 FALSE 將禁用 MDI 選項卡的自動着色 15 mdiTabParams.m_bDocumentMenu = TRUE; // 在選項卡區域的右邊緣啟用文檔菜單 16 EnableMDITabbedGroups(TRUE, mdiTabParams); 17 18 m_wndRibbonBar.Create(this); 19 m_wndRibbonBar.LoadFromResource(IDR_RIBBON); 20 21 if (!m_wndStatusBar.Create(this)) 22 { 23 TRACE0("未能創建狀態欄\n"); 24 return -1; // 未能創建 25 } 26 27 CString strTitlePane1; 28 CString strTitlePane2; 29 bNameValid = strTitlePane1.LoadString(IDS_STATUS_PANE1); 30 ASSERT(bNameValid); 31 bNameValid = strTitlePane2.LoadString(IDS_STATUS_PANE2); 32 ASSERT(bNameValid); 33 m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE), strTitlePane1); 34 m_wndStatusBar.AddExtendedElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE2, strTitlePane2, TRUE), strTitlePane2); 35 36 // 啟用 Visual Studio 2005 樣式停靠窗口行為 37 CDockingManager::SetDockingMode(DT_SMART); 38 // 啟用 Visual Studio 2005 樣式停靠窗口自動隱藏行為 39 EnableAutoHidePanes(CBRS_ALIGN_ANY); 40 41 // 啟用增強的窗口管理對話框 42 EnableWindowsDialog(ID_WINDOW_MANAGER, ID_WINDOW_MANAGER, TRUE); 43 44 // 將文檔名和應用程序名稱在窗口標題欄上的順序進行交換。這 45 // 將改進任務欄的可用性,因為顯示的文檔名帶有縮略圖。 46 ModifyStyle(0, FWS_PREFIXTITLE); 47 48 return 0; 49 }
沒錯,就是CMDITabInfo!找到她了!
10-16是對彩色的選項卡進行的設置。想要屏蔽關閉按鈕,要修改為:
1 CMDITabInfo mdiTabParams; 2 mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // 其他可用樣式... 3 //mdiTabParams.m_bActiveTabCloseButton = TRUE; // 設置為 FALSE 會將關閉按鈕放置在選項卡區域的右側 4 mdiTabParams.m_bTabCloseButton=FALSE; 5 mdiTabParams.m_bTabIcons = FALSE; // 設置為 TRUE 將在 MDI 選項卡上啟用文檔圖標 6 mdiTabParams.m_bAutoColor = TRUE; // 設置為 FALSE 將禁用 MDI 選項卡的自動着色 7 mdiTabParams.m_bDocumentMenu = TRUE; // 在選項卡區域的右邊緣啟用文檔菜單 8 EnableMDITabbedGroups(TRUE, mdiTabParams);
這樣,就完成了。

=======================================================================
網上有關禁用MDI選項卡關閉按鈕的文章,大多抄襲自MSDN的這篇文檔http://support.microsoft.com/kb/201553/zh-cn
但是,不適用於VS2010下使用Office風格的MDI程序——該文檔只更新到2006年11月21日。
=======================================================================
本文一步一步的解析實現,故暫不提供示例工程源代碼。
