65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 CodeProject - 应用程序的一天 - 第 5 部分,共 5 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (17投票s)

2007 年 1 月 27 日

CPOL

11分钟阅读

viewsIcon

64875

downloadIcon

520

使用 CodeProject 进行偶尔支持的正确编码方式。

第5部分(共5部分) - 引言

指向系列其他部分的链接

以下文字与第1部分相同。如果您还没有阅读过那篇文章,那么本文对您来说将毫无用处,所以请务必补上。我们在这里等您。如果您已经阅读了第1部分,您可以跳过这些介绍部分

本系列文章是我的“我们实际使用的代码”系列中的又一篇。没有不必要的理论讨论,没有技术阐述,也没有因为我独立思考而自吹自擂。它只是我为了启动我们的一个应用程序而做的一堆事情。本文中的大部分内容都是基于我从CodeProject获取的其他代码,下文描述了我正在积极开发的一个项目的基础以及我是如何整合从CodeProject获得的必威和帮助的。

抱怨

我作为CodeProject的会员已经超过六年了(截至本文撰写之时),我发现了一些关于文章的令人不安的趋势。首先,文章作者倾向于发表一篇文章,随着时间的推移,作者基本上放弃了这篇文章,提问的人要么得不到作者的回应,要么得到一个类似于“我不再用这种/那种语言编程了”的回复。说实话,你不能责怪他们。我使用的许多文章都是三四年前的,我理解程序员需要进步,这通常意味着彻底放弃旧代码。

另一方面,是那些下载与给定文章相关的源代码和示例的人。很多时候,有人会在一篇文章中提出一个与文章本身毫无关系的问题,但主题会与文章的某个方面相关。例如,我发表了一篇关于动态构建菜单的文章。最近,有人在那篇文章中发布了一条消息,询问如何将winhelp添加到他们的动态构建菜单中。还有一些人,他们遇到(真实或想象的)文章问题,并期望别人为他们解决。这些人真的让我很恼火。毕竟,我们这里都应该是程序员。

那么,本文的要点是什么?

本文的全部目的是为了说明我过去六年从CodeProject收集的代码片段、类和技术在实际世界中的应用,包括为了满足我有时奇怪的需求而对代码进行的变通处理。很多时候,我会使用VC++论坛提问,以帮助我理解一篇文章,或者修改文章的代码以供我自己使用。

假设

本文的最初版本是一个详细的教程,描述了如何使用IDE以及其他一些无关紧要的项目。过了一段时间,我意识到这在文章的篇幅方面造成了巨大的开销。除此之外,我对整个事情开始感到厌倦,我清楚地看到我的写作质量因此开始下降。

唯一的解决办法是重新开始,并假设您,用户,对VS2005 IDE有基本的了解,特别是它与创建VC++/MFC应用程序相关的内容。这样,我们就可以更多地讨论重要的事情,而不必忍受您应该已经知道的事情。我还假设您对MFC有良好的工作知识。我不是说您必须是专家,但我假设您可以在MFC项目中自由操作,而不会被CMainFrame的复杂性所困扰。

其他

文章中散布着“编码笔记”。这些笔记只是描述了我编码时的方式以及我这样做的原因。它们绝不是任何意义上的要求,但它们通常涉及代码的可读性和可维护性。我相信你们许多人都有自己的行事方式,但请将关于这些问题的评论保持在最低限度。毕竟,本文不是关于风格的。

编写完整的演示应用程序的整个过程只需要几个小时(如果您提前知道所有步骤)。撰写本系列文章花了我数天时间,所以不要被它的长度吓到。

本文的 HTML 和图像包含在项目下载中,但不包含漂亮的 CodeProject 格式。如果您能在心理上处理好这一点,您可以直接参考此 HTML 文件继续您的编程。

最后,我知道有些人只是因为这是我写的东西而给我的东西投1票。我请求您成熟专业,在投票时将您的政治观点限制在论坛上。请记住,您投的是文章的票,而不是作者的票。

我们已经完成了什么

在本系列文章的第1部分中,我们逐步创建了一个MFC SDI应用程序,并通过添加MFC网格控件使其视图更加有趣。在第2部分中,我们添加了一个拆分窗口,并能够在一个拆分窗格中交换视图。在第3部分中,我们添加了一个自定义状态栏类和一些简单的多线程功能,用于更新状态栏窗格的内容。在第4部分中,我们创建了一个MFC扩展DLL来包含所有视图,并创建了两个新应用程序,每个应用程序都使用新的扩展DLL。

在本文中,我将快速介绍我添加到应用程序的最后几项内容。由于这些项目大部分都在各自的文章中得到了充分的描述,所以我将只对其进行概述。此外,我从事这个系列文章已经足够久了(我开始有点眼花缭乱了)。到现在,我希望您已经明白了我一直试图灌输给您的大致思想——CodeProject是一个很好的起点,但您通常需要付出适度的努力才能让文章适用于您的应用程序,最终,您真正能依靠的只有自己。

清理一些遗留问题

我们现在拥有了我们花哨的小应用程序,对自己能够满足管理层的任何需求而感到沾沾自喜。当我们凝视着我们应用程序的“辉煌”时,我们开始注意到在新的开发狂潮中我们忽略(或者我更喜欢说——“推迟了我们的注意力”)的小细节。

我们要处理的第一件事是清理标题栏。它包含文本“Untitled”,由于我们实际上不加载“文档”(这是一个数据库的前端,记得吗?),我们可以安全地将其从标题栏中删除。

由于我不记得具体如何操作,我在CodeProject上搜索了“Untitled”一词,并找到了Michael Dunn的C++ FAQ的链接。具体来说,我对这个FAQ条目很感兴趣。因此,对于我们的三个应用程序,将以下代码添加到PreCreateWindow函数中

    // don't show the document name in the titlebar
    cs.style &= ~FWS_ADDTOTITLE;

可选更改 - 我们的要求之一是应用程序以最大化窗口启动。原因是这些应用程序旨在通过终端服务使用并从应用程序服务器运行。要使您的应用程序窗口最初最大化显示,请更改应用程序InitInstance()函数中的这一行

    // The one and only window has been initialized, so show and update it
    m_pMainWnd->ShowWindow(SW_SHOW); // <----------Change this line
    m_pMainWnd->UpdateWindow();

    // The one and only window has been initialized, so show and update it
    m_pMainWnd->ShowWindow(SW_MAXIMIZE);
    m_pMainWnd->UpdateWindow();

最后,我在CPrimaryView类中添加了一些代码,以便网格可以打印一些行。它只是一个添加十行到网格的for循环,但如果您确实需要查看代码,请查看CPrimaryView::OnInitialUpdate()

一个基于XML的应用程序配置文件类

为了使本文与CodeProject相关,我在网站上搜索了一圈,没有找到我认为合适的,所以我自己编写了一个类(看,我是一个程序员,程序员就是这样做的),然后发表了一篇文章。当然,这里还有另一篇XMLprofile文章,但它根本无法编译。如果您还没有,请查看我的XML应用程序配置文件类文章。

CProfile 类允许您创建/使用在用法上类似于INI文件的XML文件。它不是一个包罗万象的XML文件读取器,但它能很好地完成其设计任务。我使用这个类来读取线程间隔的程序设置。在这个示例应用程序中,只需三行代码就可以完成设置。

添加打印支持

对于CPrimaryView

由于MFC网格控件内置了打印支持,我们所要做的就是调用该函数。由于我是手动创建CPrimaryView的,所以我不得不将SDIClientView.cpp文件中的打印内容复制到CPrimaryView类中,并重写OnFilePrint。在OnFilePrint中,我调用了网格控件的Print函数。

对于CSecondaryView

对于CSecondaryView,事情有点棘手。同样,由于我是手动创建该类的,它没有附带必要的打印功能,所以我再次从SSDIClientView类中复制/粘贴。

我在CodeProject上找不到任何能满足需求的东西,所以我不得不“打破常规”去寻找。万一我在CodeProject上找不到需要的东西,我就会去CodeGuru上寻找。我最终在CodeGuru上找到了一个名为PrivatePrint的类,作者是Rob A. Fraydl(没有链接,因为它不是CodeProject文章)。我正在寻找一种能让我打印页眉和页脚以及当前窗口内容的方法,这个类几乎是完美的。我唯一需要添加的是一种打印超出纸张宽度的文本行的方法(请参阅此类中的PrintEx()函数)。

我的附加函数增加了在单词边界(每个空格处)换行的功能,这样我就可以在打印页面上自动换行文本。为了实现文本换行,我使用了我的QStringParser文章。

添加到系统菜单

由于显示应用程序没有菜单,我们可能希望向系统菜单添加一些菜单功能。系统菜单是当您点击标题栏中应用程序图标时显示的菜单。更改系统菜单的操作相当简单。只需将以下代码添加到您的应用程序的InitInstance函数中

HMENU pSysMenu = ::GetSystemMenu(m_pMainWnd->GetSafeHwnd(), FALSE);
if (pSysMenu)
{
    ::InsertMenu(pSysMenu, 0, MF_BYPOSITION | MF_STRING, ID_SHOW_MESSAGE1, 
                 "Show Message 1");
    ::InsertMenu(pSysMenu, 1, MF_BYPOSITION | MF_STRING, ID_SHOW_MESSAGE2, 
                 "Show Message 2");
    ::InsertMenu(pSysMenu, 2, MF_BYPOSITION | MF_SEPARATOR, 0, "");
}

检索菜单后,只需将新的菜单项添加到其中。在上面的代码中,我们将自定义菜单项添加到了菜单的顶部,并用菜单分隔符将它们与原始菜单项分隔开。很简单。

接下来,我们需要将菜单项ID添加到resource.h文件中。最简单的方法是简单地在应用程序资源中添加一个新菜单,并在新菜单中创建菜单项。好吧,这有点容易。当我这样做,然后尝试通过Properties视图为CMainFrame消息映射添加处理程序时,ID显示为32771和32772,而不是我给它们的名字。我不得不回去重新显示菜单资源并更改ID名称,以便它们能正确地显示在消息映射中。有时VS2005就是让我很恼火。无论如何,我随后返回并在CMainFrame中添加了处理程序。

最后一步是为Windows WM_SYSCOMMAND 消息添加处理程序。添加处理程序后,将函数内容替换为以下代码

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
    switch (nID)
    {
        case ID_SHOW_MESSAGE1 :
        case ID_SHOW_MESSAGE2 :
            PostMessage(WM_COMMAND, nID, 0);
            break;
        default :
            CFrameWnd::OnSysCommand(nID, lParam);
            break;
    }
}

此版本的函数通过将我们自己的命令反射回 CMainFrame 来处理它们。任何不是我们自己的消息都被认为是系统命令,并在“默认”情况下处理。如果您想在将系统命令传递给基类之前执行一些自定义处理,请参阅MSDN关于发送到此函数的系统命令ID的文档。它没有列出所有命令,因此您可能需要在网上搜索更详细的信息。

如果您想全面了解我在编写此代码时关于VS2005的发现,请查看我的修改系统菜单文章。与MFC的许多其他部分一样,对OnSysCommand函数进行了一些更改,如果您计划在自己的应用程序中实现系统菜单修改,则应注意这些更改。

第5部分完

由于本文篇幅较长,我决定将其分为几个部分。如果网站编辑按照我的要求操作,所有后续部分都应该位于网站的同一代码区。每个部分都有自己的源代码,因此在阅读后续部分时,请务必下载该部分的源代码(除非您手动完成我在此文中概述的所有内容)。

为了保持一致性(和理智),请对所有部分进行投票,并以相同的方式投票。这有助于将文章保留在同一部分。感谢您的理解。

© . All rights reserved.