• 魔改——MFC SDI程序 转换为 MDI程序


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

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

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

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

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

    本文链接: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);
  • 相关阅读:
    Tengine vs openresty
    知名黑客组织Anonymous(匿名者)的装备库
    25个让Java程序员更高效的Eclipse插件
    php提示Fatal error: Call to undefined function imagecreate()
    【转】【iOS】动态更换App图标
    unity在安卓中横屏闪退
    WWW缓存方式
    if UNITY_EDITOR这个判断常用,还有哪个常用捏?
    Lerp和SmoothDamp比较
    UNITY把3D模型显示在UI层级上的思路
  • 原文地址:https://www.cnblogs.com/wlsandwho/p/4286906.html
Copyright © 2020-2023  润新知