==================================声明==================================
本文原创,转载在正文中显要的注明作者和出处,并保证文章的完整性。
未经作者同意请勿修改(包括本声明),保留法律追究的权利。
未经作者同意请勿用于出版、印刷或学术引用。
本文不定期修正完善,为保证内容正确,建议移步原文处阅读。
本文链接:http://www.cnblogs.com/wlsandwho/p/4286906.html
=======================================================================
之前有个程序,写的时候用的SDI然后分割视图左侧创建Outlook工具栏右侧不同视图。现在打算改成MDI。毕竟使用Ribbon的MDI还是很好看的。
=======================================================================
网上找了找资料,好多都是VC6时代的,不是很好。所以自己写一个留着,万一以后忘了呢?!
基于VS2010,MFC,未打SP1。
=======================================================================
先创建一个SDI,待会把它修改为MDI。
=======================================================================
下面创建一个MDI,作为参照。
=======================================================================
有一个事情一定要记住,既然要改,肯定是要把所有S的改成M的!所以,最后不放心可以用查找再找一遍。
魔改开始!
=======================================================================
看TestSDISample工程的InitInstance函数。
1 // CTestSDISampleApp 初始化 2 3 BOOL CTestSDISampleApp::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(FALSE); 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 CSingleDocTemplate* pDocTemplate; 56 pDocTemplate = new CSingleDocTemplate( 57 IDR_MAINFRAME, 58 RUNTIME_CLASS(CTestSDISampleDoc), 59 RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口 60 RUNTIME_CLASS(CTestSDISampleView)); 61 if (!pDocTemplate) 62 return FALSE; 63 AddDocTemplate(pDocTemplate); 64 65 66 // 分析标准 shell 命令、DDE、打开文件操作的命令行 67 CCommandLineInfo cmdInfo; 68 ParseCommandLine(cmdInfo); 69 70 71 72 // 调度在命令行中指定的命令。如果 73 // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。 74 if (!ProcessShellCommand(cmdInfo)) 75 return FALSE; 76 77 // 唯一的一个窗口已初始化,因此显示它并对其进行更新 78 m_pMainWnd->ShowWindow(SW_SHOW); 79 m_pMainWnd->UpdateWindow(); 80 // 仅当具有后缀时才调用 DragAcceptFiles 81 // 在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生 82 return TRUE; 83 }
再对照TestMDIReference的InitInstance函数。
1 // CTestMDIReferenceApp 初始化 2 3 BOOL CTestMDIReferenceApp::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_TestMDIReferencTYPE, 57 RUNTIME_CLASS(CTestMDIReferenceDoc), 58 RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 59 RUNTIME_CLASS(CTestMDIReferenceView)); 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 }
现在我们动手,先把
CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTestSDISampleDoc), RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口 RUNTIME_CLASS(CTestSDISampleView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate);
中的CSingleDocTemplate改成CMultiDocTemplate,CMainFrame改成CChildFrame。顺便复制粘贴创建主框架窗口的代码。
理论性的东西看MFC的相关书籍吧,我就记着
CMainFrame=>CChildFrame=>(Template=Document + View),
而我们new的时候,在结构上可以理解为一下子做了后两部分,当然,实际上不是啊,好复杂的讲不了……
1 // 注册应用程序的文档模板。文档模板 2 // 将用作文档、框架窗口和视图之间的连接 3 // CSingleDocTemplate* pDocTemplate; 4 // pDocTemplate = new CSingleDocTemplate( 5 // IDR_MAINFRAME, 6 // RUNTIME_CLASS(CTestSDISampleDoc), 7 // RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口 8 // RUNTIME_CLASS(CTestSDISampleView)); 9 // if (!pDocTemplate) 10 // return FALSE; 11 // AddDocTemplate(pDocTemplate); 12 CMultiDocTemplate* pDocTemplate; 13 pDocTemplate = new CMultiDocTemplate( 14 IDR_MAINFRAME, 15 RUNTIME_CLASS(CTestSDISampleDoc), 16 RUNTIME_CLASS(/*CMainFrame*/CChildFrame), // 主 SDI 框架窗口 17 RUNTIME_CLASS(CTestSDISampleView)); 18 if (!pDocTemplate) 19 return FALSE; 20 AddDocTemplate(pDocTemplate); 21 // 创建主 MDI 框架窗口 22 CMainFrame* pMainFrame = new CMainFrame; 23 if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME)) 24 { 25 delete pMainFrame; 26 return FALSE; 27 } 28 m_pMainWnd = pMainFrame; 29 // 仅当具有后缀时才调用 DragAcceptFiles 30 // 在 MDI 应用程序中,这应在设置 m_pMainWnd 之后立即发生
F7,看输出。
1 1>------ 已启动生成: 项目: TestSDISample, 配置: Debug Win32 ------ 2 1>生成启动时间为 2015/2/11 21:24:22。 3 1>InitializeBuildStatus: 4 1> 正在对“DebugTestSDISample.unsuccessfulbuild”执行 Touch 任务。 5 1>ClCompile: 6 1> 所有输出均为最新。 7 1> TestSDISample.cpp 8 1>c:wlscodehome estsdisample estsdisample estsdisample.cpp(125): error C2653: “CChildFrame”: 不是类或命名空间名称 9 1>c:wlscodehome estsdisample estsdisample estsdisample.cpp(131): error C2248: “CMainFrame::CMainFrame”: 无法访问 protected 成员(在“CMainFrame”类中声明) 10 1> c:wlscodehome estsdisample estsdisamplemainfrm.h(21) : 参见“CMainFrame::CMainFrame”的声明 11 1> c:wlscodehome estsdisample estsdisamplemainfrm.h(17) : 参见“CMainFrame”的声明 12 1> 13 1>生成失败。 14 1> 15 1>已用时间 00:00:00.15 16 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
第一个好处理,工程里没有的,就从MDI里复制粘贴一份,加进工程里就是了。(就不截图了。)
修改复制来的ChildFrm.cpp文件
#include "stdafx.h" //#include "TestMDIReference.h" #include "TestSDISample.h"
第二个,无法访问protected成员,就改成public的。
1 //protected: // 仅从序列化创建 2 public:
另外,参照MDI工程CMainFrame的声明,将基类修改为CMDIFrameWndEx,既然基类修改了,那么CMainFrame中就不能够再出现CFrameWndEx,所以省事的方法就是全部查找替换。
F7,无错误警告,运行。
点击新建
发先多视图是有了,但不是华丽好看的Tab页。
将MDI里CMainFrame::OnCreate中代码复制到SDI里CMainFrame::OnCreate相应的位置。
1 CMDITabInfo mdiTabParams; 2 mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // 其他可用样式... 3 mdiTabParams.m_bActiveTabCloseButton = TRUE; // 设置为 FALSE 会将关闭按钮放置在选项卡区域的右侧 4 mdiTabParams.m_bTabIcons = FALSE; // 设置为 TRUE 将在 MDI 选项卡上启用文档图标 5 mdiTabParams.m_bAutoColor = TRUE; // 设置为 FALSE 将禁用 MDI 选项卡的自动着色 6 mdiTabParams.m_bDocumentMenu = TRUE; // 在选项卡区域的右边缘启用文档菜单 7 EnableMDITabbedGroups(TRUE, mdiTabParams);
复制到
1 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 2 { 3 if (/*CFrameWndEx*/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("未能创建状态栏 "); 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 return 0; 42 }
F7,成功。
点击新建
=======================================================================
PS:
1 选项卡的标题在
1 pDocTemplate = new CMultiDocTemplate( 2 IDR_MAINFRAME, 3 RUNTIME_CLASS(CTestSDISampleDoc), 4 RUNTIME_CLASS(/*CMainFrame*/CChildFrame), // 主 SDI 框架窗口 5 RUNTIME_CLASS(CTestSDISampleView));
的参数1中设置。
详见上一篇博文,标题很长的那个。
2 尚未搞清楚下面的代码(MDI的CMainFrame::OnCreate中的最后),所以没有添加。
1 // 启用增强的窗口管理对话框 2 EnableWindowsDialog(ID_WINDOW_MANAGER, ID_WINDOW_MANAGER, TRUE); 3 4 // 将文档名和应用程序名称在窗口标题栏上的顺序进行交换。这 5 // 将改进任务栏的可用性,因为显示的文档名带有缩略图。 6 ModifyStyle(0, FWS_PREFIXTITLE);