==================================声明==================================

本文原创,转载在正文中显要的注明作者和出处,并保证文章的完整性。

未经作者同意请勿修改(包括本声明),保留法律追究的权利。

未经作者同意请勿用于出版、印刷或学术引用。

本文不定期修正完善,为保证内容正确,建议移步原文处阅读。

本文链接:http://www.cnblogs.com/wlsandwho/p/4286906.html

=======================================================================

之前有个程序,写的时候用的SDI然后分割视图左侧创建Outlook工具栏右侧不同视图。现在打算改成MDI。毕竟使用Ribbon的MDI还是很好看的。

=======================================================================

网上找了找资料,好多都是VC6时代的,不是很好。所以自己写一个留着,万一以后忘了呢?!

基于VS2010,MFC,未打SP1。

=======================================================================

先创建一个SDI,待会把它修改为MDI。

=======================================================================

下面创建一个MDI,作为参照。

=======================================================================

有一个事情一定要记住,既然要改,肯定是要把所有S的改成M的!所以,最后不放心可以用查找再找一遍。

魔改开始!

=======================================================================

看TestSDISample工程的InitInstance函数。

 // CTestSDISampleApp 初始化

 BOOL CTestSDISampleApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls); CWinAppEx::InitInstance(); // 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
} AfxEnableControlContainer(); EnableTaskbarInteraction(FALSE); // 使用 RichEdit 控件需要 AfxInitRichEdit2()
// AfxInitRichEdit2(); // 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
LoadStdProfileSettings(); // 加载标准 INI 文件选项(包括 MRU) InitContextMenuManager(); InitKeyboardManager(); InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams); // 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestSDISampleDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CTestSDISampleView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate); // 分析标准 shell 命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo); // 调度在命令行中指定的命令。如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE; // 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
// 仅当具有后缀时才调用 DragAcceptFiles
// 在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
return TRUE;
}

再对照TestMDIReference的InitInstance函数。

 // CTestMDIReferenceApp 初始化

 BOOL CTestMDIReferenceApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls); CWinAppEx::InitInstance(); // 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
} AfxEnableControlContainer(); EnableTaskbarInteraction(); // 使用 RichEdit 控件需要 AfxInitRichEdit2()
// AfxInitRichEdit2(); // 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
LoadStdProfileSettings(); // 加载标准 INI 文件选项(包括 MRU) InitContextMenuManager(); InitKeyboardManager(); InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams); // 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_TestMDIReferencTYPE,
RUNTIME_CLASS(CTestMDIReferenceDoc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
RUNTIME_CLASS(CTestMDIReferenceView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate); // 创建主 MDI 框架窗口
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// 仅当具有后缀时才调用 DragAcceptFiles
// 在 MDI 应用程序中,这应在设置 m_pMainWnd 之后立即发生 // 分析标准 shell 命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo); // 调度在命令行中指定的命令。如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 主窗口已初始化,因此显示它并对其进行更新
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow(); return TRUE;
}

现在我们动手,先把

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的时候,在结构上可以理解为一下子做了后两部分,当然,实际上不是啊,好复杂的讲不了……

     // 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
// CSingleDocTemplate* pDocTemplate;
// pDocTemplate = new CSingleDocTemplate(
// IDR_MAINFRAME,
// RUNTIME_CLASS(CTestSDISampleDoc),
// RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
// RUNTIME_CLASS(CTestSDISampleView));
// if (!pDocTemplate)
// return FALSE;
// AddDocTemplate(pDocTemplate);
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestSDISampleDoc),
RUNTIME_CLASS(/*CMainFrame*/CChildFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CTestSDISampleView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 创建主 MDI 框架窗口
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// 仅当具有后缀时才调用 DragAcceptFiles
// 在 MDI 应用程序中,这应在设置 m_pMainWnd 之后立即发生

F7,看输出。

 >------ 已启动生成: 项目: TestSDISample, 配置: Debug Win32 ------
>生成启动时间为 // ::。
>InitializeBuildStatus:
> 正在对“Debug\TestSDISample.unsuccessfulbuild”执行 Touch 任务。
>ClCompile:
> 所有输出均为最新。
> TestSDISample.cpp
>c:\wlscodehome\testsdisample\testsdisample\testsdisample.cpp(): error C2653: “CChildFrame”: 不是类或命名空间名称
>c:\wlscodehome\testsdisample\testsdisample\testsdisample.cpp(): error C2248: “CMainFrame::CMainFrame”: 无法访问 protected 成员(在“CMainFrame”类中声明)
> c:\wlscodehome\testsdisample\testsdisample\mainfrm.h() : 参见“CMainFrame::CMainFrame”的声明
> c:\wlscodehome\testsdisample\testsdisample\mainfrm.h() : 参见“CMainFrame”的声明
>
>生成失败。
>
>已用时间 ::00.15
========== 生成: 成功 个,失败 个,最新 个,跳过 个 ==========

第一个好处理,工程里没有的,就从MDI里复制粘贴一份,加进工程里就是了。(就不截图了。)

修改复制来的ChildFrm.cpp文件

#include "stdafx.h"
//#include "TestMDIReference.h"
#include "TestSDISample.h"

第二个,无法访问protected成员,就改成public的。

 //protected: // 仅从序列化创建
public:

另外,参照MDI工程CMainFrame的声明,将基类修改为CMDIFrameWndEx,既然基类修改了,那么CMainFrame中就不能够再出现CFrameWndEx,所以省事的方法就是全部查找替换。

F7,无错误警告,运行。

点击新建

发先多视图是有了,但不是华丽好看的Tab页。

将MDI里CMainFrame::OnCreate中代码复制到SDI里CMainFrame::OnCreate相应的位置。

     CMDITabInfo mdiTabParams;
mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // 其他可用样式...
mdiTabParams.m_bActiveTabCloseButton = TRUE; // 设置为 FALSE 会将关闭按钮放置在选项卡区域的右侧
mdiTabParams.m_bTabIcons = FALSE; // 设置为 TRUE 将在 MDI 选项卡上启用文档图标
mdiTabParams.m_bAutoColor = TRUE; // 设置为 FALSE 将禁用 MDI 选项卡的自动着色
mdiTabParams.m_bDocumentMenu = TRUE; // 在选项卡区域的右边缘启用文档菜单
EnableMDITabbedGroups(TRUE, mdiTabParams);

复制到

 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (/*CFrameWndEx*/CMDIFrameWndEx::OnCreate(lpCreateStruct) == -)
return -; BOOL bNameValid;
// 基于持久值设置视觉管理器和样式
OnApplicationLook(theApp.m_nAppLook); CMDITabInfo mdiTabParams;
mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // 其他可用样式...
mdiTabParams.m_bActiveTabCloseButton = TRUE; // 设置为 FALSE 会将关闭按钮放置在选项卡区域的右侧
mdiTabParams.m_bTabIcons = FALSE; // 设置为 TRUE 将在 MDI 选项卡上启用文档图标
mdiTabParams.m_bAutoColor = TRUE; // 设置为 FALSE 将禁用 MDI 选项卡的自动着色
mdiTabParams.m_bDocumentMenu = TRUE; // 在选项卡区域的右边缘启用文档菜单
EnableMDITabbedGroups(TRUE, mdiTabParams); m_wndRibbonBar.Create(this);
m_wndRibbonBar.LoadFromResource(IDR_RIBBON); if (!m_wndStatusBar.Create(this))
{
TRACE0("未能创建状态栏\n");
return -; // 未能创建
} CString strTitlePane1;
CString strTitlePane2;
bNameValid = strTitlePane1.LoadString(IDS_STATUS_PANE1);
ASSERT(bNameValid);
bNameValid = strTitlePane2.LoadString(IDS_STATUS_PANE2);
ASSERT(bNameValid);
m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE), strTitlePane1);
m_wndStatusBar.AddExtendedElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE2, strTitlePane2, TRUE), strTitlePane2); // 启用 Visual Studio 2005 样式停靠窗口行为
CDockingManager::SetDockingMode(DT_SMART);
// 启用 Visual Studio 2005 样式停靠窗口自动隐藏行为
EnableAutoHidePanes(CBRS_ALIGN_ANY); return ;
}

F7,成功。

点击新建

=======================================================================

PS:

1 选项卡的标题在

     pDocTemplate = new CMultiDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestSDISampleDoc),
RUNTIME_CLASS(/*CMainFrame*/CChildFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CTestSDISampleView));

的参数1中设置。

详见上一篇博文,标题很长的那个

2 尚未搞清楚下面的代码(MDI的CMainFrame::OnCreate中的最后),所以没有添加。

     // 启用增强的窗口管理对话框
EnableWindowsDialog(ID_WINDOW_MANAGER, ID_WINDOW_MANAGER, TRUE); // 将文档名和应用程序名称在窗口标题栏上的顺序进行交换。这
// 将改进任务栏的可用性,因为显示的文档名带有缩略图。
ModifyStyle(, FWS_PREFIXTITLE);

魔改——MFC SDI程序 转换为 MDI程序的更多相关文章

  1. 魔改——MFC SDI 支持 内嵌 EXCEL OLE

    ==================================声明================================== 本文版权归作者所有 未经作者授权 请勿转载 保留法律追究的 ...

  2. 魔改——MFC MDI程序 定制 文档模板 运行时全部打开 禁用关闭按钮

    ==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...

  3. VC++ MFC单文档应用程序SDI下调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错原因分析及解决办法:glewInit()初始化的错误

    1.问题症状 在VC++环境下,利用MFC单文档应用程序SDI下开发OpenGL程序,当调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错,出错代码如下: OpenG ...

  4. MFC通过ODBC连接Mysql程序

    分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 MFC通过ODBC连接 ...

  5. 魔改——MDI多视图模板Tab/标签页 初始化/操作控件

    ==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...

  6. JAVA应用程序转换为Applet

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/41673295 将一个图形的JAVA应用程序转换为能够嵌入在 ...

  7. windows mfc 程序,不同程序通信和互斥

    1. 共享内存(项目中使用过) 我转备份文章:http://www.cnblogs.com/swing07/p/8087686.html CreateFileMapping 或 OpenFileMap ...

  8. MFC学习(三)程序入口和执行流程

    1) WIN32 API程序当中,程序入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用程序窗口函数.而在MFC程序当中我们 ...

  9. [转帖]reptyr, 将正在运行的程序转换为新终端

    reptyr, 将正在运行的程序转换为新终端 https://www.helplib.com/GitHub/article_45241 学习一下. 很抑郁的是 没有 arm64和龙芯平台的二进制文件. ...

随机推荐

  1. 三、Authentication & sessionid

    客户在访问Django的某些敏感资料时,被要求需要先登录,客户通过/admin/login进行登录,客户登录成功后,Django给客户分配一个sessionid,后续的访问过程,客户端只需在http头 ...

  2. (9)分布式下的爬虫Scrapy应该如何做-关于ajax抓取的处理(一)

    转载请注明出处:http://www.cnblogs.com/codefish/p/4993809.html 最近在群里频繁的被问到ajax和js的处理问题,我们都知道,现在很多的页面都是用动态加载的 ...

  3. 使用Architecture Explorer分析应用程序及使用层次图

    使用Architecture Explorer分析应用程序 Architecture Explorer和依赖图可以帮助我们了解所有的项目,包括小项目和大项目.Architecture Explorer ...

  4. SQL 日期转换(阳历转阴历)

    --步骤:创建日期表,放初始放初始化资料 --因为农历的日,是由天文学家推算出来,到现在只有到年,以后的有了还可以加入! if object_id('SolarData') is not nulldr ...

  5. C#如何使用异步编程

    怎么使用异步,就是用委托进行处理,如果委托对象在调用列表中只有一个方法,它就可以异步执行这个方法.委托类有两个方法,叫做BeginInvoke和EndInvoke,它们是用来异步执行使用. 异步有三种 ...

  6. 【BZOJ 4326】【NOIP2015】运输计划

    http://www.lydsy.com/JudgeOnline/problem.php?id=4326 题目描述 公元2044年,人类进入了宇宙纪元. 国有个星球,还有条双向航道,每条航道建立在两个 ...

  7. 线上mysql内存持续增长直至内存溢出被killed分析(已解决)

    来新公司前,领导就说了,线上生产环境Mysql库经常会发生日间内存爆掉被killed的情况,结果来到这第一天,第一件事就是要根据线上服务器配置优化配置,同时必须找出现在mysql内存持续增加爆掉的原因 ...

  8. [WP8] ListBox的Item宽度自动填满

    [WP8] ListBox的Item宽度自动填满 范例下载 范例程序代码:点此下载 问题情景 开发WP8应用程序的时候,常常会需要使用ListBox作为容器来呈现各种数据集合.但是在ListBox呈现 ...

  9. 备份一张iPhone拍照写入exif中的orientation图片

  10. swift学习笔记之-析构过程

    //析构过程deist import UIKit /*析构过程(Deinitialization):析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用.析构器用关键字deinit来标示 ...