⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap7_2.htm

📁 VC++编程实例。非常详细
💻 HTM
📖 第 1 页 / 共 5 页
字号:
        shell commands, DDE, file open</font></p>
        <p><font >CCommandLineInfo cmdInfo;</font></p>
        <p><font >ParseCommandLine(cmdInfo);</font></p>
        <p><font >// Dispatch commands specified on the
        command line</font></p>
        <p><font >if (!ProcessShellCommand(cmdInfo))</font></p>
        <p><font >return FALSE;</font></p>
        <p><font >// The one and only window has been
        initialized, so show and update it.</font></p>
        <p><font >m_pMainWnd-&gt;ShowWindow(SW_SHOW);</font></p>
        <p><font >m_pMainWnd-&gt;UpdateWindow();</font></p>
        <p><font >// Enable drag/drop open</font></p>
        <p><font >m_pMainWnd-&gt;DragAcceptFiles();</font></p>
        <p><font >}</font></p>
          <p><font >  在InitInstance中,首先声明一个CSingleDocTemplate*类型的单文档模板对象指针(因为这里的文本编辑器使用单文档界面)。然后创建该类型的模板对象。如果要使用多文档界面,只需要将这里的CSingleDocTemplate改为CMultiDocTemplate,当然CMainFrame也要改为从CFrameWnd改为CMDIChildWnd或其派生类。</font></p>
          <p><font >  在CSingleDocTemplate构造函数中,还包含一个IDR_MAINFRAME参数。它指向一个字符串资源,这个字符串给出了文档所使用及显示时所要求的几个选项,包括文档名字、文档的文件扩展名、在框架窗口上显示的名字等等,我们称之为文档模板字符串。有关文档模板字符串还将在下一章使用多个文档模板这一节作详细阐述,因此这里就不展开讲了。</font></p>
          <p><font >  然后InitInstance调用AddDocTemplate将创建好的文档模板加入到应用程序可用的文档模板链表中去。这样,如果用户选择了File-New或File-Open菜单要求创建或打开一个文档时,应用程序类的OnNewDocument成员函数和OnOpenDocument()成员函数就可以从文档模板链表中检索出文档模板提示用户选择适当的文档类型并创建文档及相关的视图、框架窗口。</font></p>
        <p><font color="#3973DE" >文档</font></p>
          <p><font >  Editor的文档类CEditorDoc从CDocument派生下来,它规定了应用程序所用的数据。如果需要在应用程序中提供OLE功能,则需要从COleDocument或其派生类派生出自己的文档类。</font></p>
        <p><font color="#3973DE" >视图</font></p>
          <p><font >  Editor的视图类从CView派生,它是数据的用户窗口。视图规定了用户查看文档数据以及同数据交互的方式。有时一个文档可能需要多个视图。</font></p>
          <p><font >  如果文档需要卷滚,需要从CScrollView派生出视图类。如果希望视图按一个对话框模板资源来布置用户界面,可以从CFormView派生。由于CFormView经常同数据库打交道,因此我们把它放在第十章“数据库技术”中结合数据库技术讲解。感兴趣的读者可以先看看Visual 
            C++ MFC例子CHKBOOK(在SAMPLES\MFC\GENERAL\CHKBOOK目录下)。</font></p>
        <p><font color="#3973DE" >框架窗口</font></p>
          <p><font >  视图在文档框架窗口中显示,它是框架窗口的子窗口。框架窗口作用有二:一是为视图提供可视的边框,还包括标题条、一些标准的窗口组件(最大、最小化按钮、关闭按钮),象一个容器一样把视图装起来。二是响应标准的窗口消息,包括最大化、最小化、调整尺寸等。当框架窗口关闭时,在其中的视图也被自动删除。视图和框架窗口关系如图7-6所示:</font></p>
        <p align="center"><img src="T7_6.gif"
        alt="T7_6.tif (129540 bytes)" width="440" height="273"></p>
        <p align="center"><font >图7-6
        视图和框架窗口的关系</font></p>
          <p><font >  对于SDI程序,文档框架窗口也就是应用程序的主框架窗口。在MDI应用程序中,文档框架窗口是显示在主框架窗口中的子窗口(通常是CMDIChildWnd或其派生类)。</font></p>
          <p><font >  可以从主框架窗口类派生出新类来包含你的视图,并指定框架的风格和其他特征。如果是SDI程序,则从CFrameWnd派生出文档框架窗口:</font></p>
        <p><font >class CMainFrame:public CFrameWnd</font></p>
        <p><font >{</font></p>
        <p><font >...</font></p>
        <p><font >};</font></p>
          <p><font >  如果是MDI窗口,则需要从CMDIFrameWnd派生出主框架窗口,同时在从CMDIChildWnd或其派生类派生出一个新类,来定制特定文档窗口的属性和功能。</font></p>
          <p><font >  在应用程序运行过程中,以上几种类型的对象相互协作,来处理命令和消息。一个且唯一的一个应用程序对象管理一个或多个文档模板,每个文档模板创建和管理一个(SDI)或多个文档(MDI)。用户通过包含在框架窗口中的视图来浏览和操作文档中的数据。在SDI应用程序中,以上对象关系如图7-7所示。</font></p>
        <p align="center"><img src="T7_7.gif"
        alt="T7_7.tif (117560 bytes)" width="344" height="237"></p>
        <p align="center"><font >图7-7 在SDI程序中各对象的关系</font></p>
        <p><font color="#3973DE">7.2.2
        设计文本编辑器的文档类</font></p>
          <p><font >  弄清这些对象的关系以后,就可以着手往框架里填写代码,实现我们的文本编辑器程序了。从以上分析可以看出,文档视结构程序的主要工作在于文档和视图的设计。</font></p>
          <p><font >  首先设计文档。程序=数据+算法,在MFC文档/视结构中,最关键的就是文档的设计。怎样保存用户输入的文本行?方法之一是保存一组指针,每个指针指向一个文本行。如果使用C语言来写这个程序的话,需要分配内存来存放这些指针,还要自己编写文本行的动态分配、增加、删除等例程。但是MFC简化这些工作,它提供了集合类(collection 
            classes)。</font></p>
          <p><font >  集合类是用来容纳和处理一组对象或标准数据类型变量的C++类。每个集合类对象可以看作一个单独的对象。类成员函数可作用于集合的所有元素。MFC提供两种类型的集合类:</font></p>
        <blockquote>
            <p><font >基于模板的集合类</font></p>
            <p><font >非基于模板的集合类</font></p>
        </blockquote>
          <p><font >  这两种集合类 对用户来说非常相似。基于模板的集合所包含的元素是用户自定义的数据结构或者说是抽象的数据结构,它以数组、链表和映射表三种方式组织用户自定义的数据结构。使用基于模板的集合类需要用户作一些类型转换工作。非基于 
            模板的集合类提供的是一组现成的、用于某种预定义的数据类型(如CObject、WORD、BYTE、DWORD、字符串等)的集合。在设计程序时,如果所用的数据类型是预定义的,如下面的编辑要用到的字符串,则使用非基于模板的集合类;如果所用得数据类型是用户自定义的数据结构类型,那就要用到基于模板的集合类。</font></p>
          <p><font >  根据对象在集合中的组织合存储方式,集合类又可分为三种类型:链表、数组、映射(或字典)。应当根据特定的编程问题,选择适当的类型。</font></p>
          <p><font >  链表:链表类用双向链表实现有序的、非索引的元素链表。链表有一个头或尾。很容易从头或尾增加或删除元素、遍历所有元素,在中间插入或删除元素。链表在需要增加、删除元素的场合效率很高。非基于模板的链表有三种:CObList、CPtrList、CStringList,分别用于管理对象指针、无类型指针和字符串。可以使用链表创建堆栈和队列。</font></p>
          <p><font >  要访问链表的成员,可以使用GetNext和GetHeadPosition()。</font></p>
          <p><font >  要删除链表的成员,可以用GetHeadPosition()和GetNext()来遍历链表,然后用delete删除其中的对象,最后调用RemoveAll删除链表所包含的指针。</font></p>
          <p><font >  数组类提供一个可动态调整数组大小的、有序的、按整数索引的对象数组。数组在内存中连续的存放固定长度的数组元素。数组的最大优点是可以随时存取任一元素。数组类包括基于模板的CArray,它可以存放任何类型的数据;MFC还为字节、字、双字、CString对象、CObject指针和无类型指针提供了预定义的类。数组的元素可以通过一个以零为基础的整数下标直接进行访问。下标操作符([])可用于设置或检取数组元素。如果要设置一个超过数组当前范围的元素,可以指定该数组是否自动增大。但是如果要调整数组大小时,则数组占用的内存块需要重新移动,效率很低。如果不要求调整数组大小,则对数组集合的访问和对标准C数组的访问一样快。在使用数组之前,应使用SetSize建立其大小,并分配内存。若不用SetSize,象数组添加元素时会导致频繁的再分配内存和拷贝数据。数组类适用于那些需要快速检索、很少需要增加或删除元素的集合。</font></p>
          <p><font >  数组通过GetAt(索引值)来访问数组中的成员。</font></p>
          <p><font >  要删除数组中的成员,可以用GetSize()取得大小,然后遍历数组中成员,用delete删除,然后调用RemoveAll()清除其中的指针数据。</font></p>
        <p><font >下面是使用数组模板类的例子:</font></p>
        <p><font >CArray&lt;CMyClass,CMyClass&amp;&gt;
        myArray;</font></p>
        <p><font >CMyClass myClass;</font></p>
        <p><font >myArray-&gt;Add(myClass);</font></p>
          <p><font >  映射类以一种字典的方式组织数据。每个元素由一个关键字和一个数值项组成,关键字用作数值项的标识符,在集合中不允许重复,必须是唯一的。如果给出一个关键字,映射类会很快找到对应的数值项。映射查找是以哈希表的方式进行的,因此在映射中查找数值项的速度很快。除了映射类模板外,预定义的映射类能支持CString对象、字、CObject指针和无类型指针。比如,CMapWordToOb类创建一个映射表对象后,就可以用WORD类型的变量作为关键字来寻找对应的CObject指针。映射类最适用于需要根据关键字进行快速检索的场合。</font></p>
          <p><font >  要访问映射中的数据,可以用GetStartPosition()定位到开始处,再用GetNextAssoc访问映射表中的成员。</font></p>
          <p><font >  要删除映射中的数据,可以用GetStartPosition和GetNextAssoc遍历并用delete删除对象,然后调用RemoveAll。</font></p>
          <p><font >  下面是使用CMap模板类的例子:</font></p>
        <p><font >CMap&lt;CString,LPCSTR,CPerson,CPerson&amp;&gt;
        myMap;</font></p>
        <p><font >CPerson person;</font></p>
        <p><font >LPCSTR lpstrName=“Tom”;</font></p>
        <p><font >myMap-&gt;SetAt(lpstrName,person);</font></p>
        <p><font >有关集合类的使用可以参见MFC的例子COLLECT。</font></p>
          <p><font >  对于文本编辑器,由于需要动态增加和删除每一行字符串,因此使用CStringList来保存文本编辑器的数据,CStringList中的每一个元素是CString类型的,它代表一行字符。可以把CString看作一个字符数组,但它提供了丰富的成员函数,比字符数组功能强大的多。</font></p>
          <p><font >  另外,还需要增加一个数据成员nLineNum,用于指示当前编辑行行号。如清单7.3,在文档类的头文件EditorDoc.h中,加入以下代码:</font></p>
        <p><font >清单7.3 CEditorDoc.h</font></p>
        <p><font >class CEditorDoc : public CDocument</font></p>
        <p><font >{</font></p>
        <p><font >protected: // create from serialization
        only</font></p>
        <p><font >CEditorDoc();</font></p>
        <p><font >DECLARE_DYNCREATE(CEditorDoc)</font></p>
        <p><font >// Attributes</font></p>
        <p><font >public:</font></p>
        <p><font >CStringList lines;</font></p>
        <p><font >int nLineNum;</font></p>
        <p><font >...</font></p>
        <p><font >};</font></p>
        <p><font >在定义了文档数据成员后,还要对文档数据成员进行初始化。</font></p>
        <p><font color="#3973DE" >初始化文档类的数据成员</font></p>
          <p><font >  当用户启动应用程序,或从应用程序的File菜单种选择New选项时,都需要对文档类的数据成员进行初始化。一般的,类的数据成员的初始化都是在构造函数中完成的,在构造函数调用结束时对象才真正存在。但对于文档来说却不同,文档类的数据成员初始化工作是在OnNewDocument成员函数中完成的,此时文档对象已经存在。为什么呢?这是因为:在单文档界面(SDI)应用程序中,在应用程序启动时,文档对象就已经被创建。文档对象直到主框架窗口被关闭时才被销毁。在用户选择File-New菜单时,应用程序对象并不是销毁原来的文档对象然后重建新的文档对象,而只是重新初始化(Re-Initialization)文档对象的数据成员,这个初始化工作就是应用程序对象的OnFileNew()消息处理成员函数通过调用OnNewDocument()函数来完成的。试想,如果把初始化数据成员的工作放在构造函数中的话,由于对象已经存在,构造函数就无法被调用,也就无法完成初始化数据成员的工作。为了避免代码的重复,在应用程序启动时,应用程序对象也是通过调用OnNewDocument成员函数来初始化文档对象的数据成员的。如果是多文档界面(MDI)程序,则数据成员的初始化也可以放到构造函数中完成。因为在MDI中,选择File-&gt;New菜单时,应用程序对象就让文档模板创建一个新文档并创建对应的框架窗口和视图。但是,为了保证应用程序在单文档和多文档界面之间的可移植性,我们还是建议将文档数据成员的初始化工作放在OnNewDocument()中完成,因为在MDI的应用程序对象的OnFileNew成员函数中,同样会调用文档对象的OnNewDocument成员函数。</font></p>
        <p><font >在OnNewDocument成员函数中手工加入代码,如清单7.4。</font></p>
        <p><font >清单7.4 OnNewDocument成员函数</font></p>
        <p><font >BOOL CEditorDoc::OnNewDocument()</font></p>
        <p><font >{</font></p>
        <p><font >if (!CDocument::OnNewDocument())</font></p>
        <p><font >return FALSE;</font></p>
        <p><font >// TODO: add reinitialization code here</font></p>
        <p><font >// (SDI documents will reuse this
        document)</font></p>
        <p><font >nLineNum=0;</font></p>
        <p><font >POSITION pos;</font></p>
        <p><font >pos=lines.GetHeadPosition();</font></p>
        <p><font >while(pos!=NULL)</font></p>
        <p><font >{</font></p>
        <p><font >((CString)lines.GetNext(pos)).Empty();</font></p>
        <p><font >}</font></p>
        <p><font >lines.RemoveAll();</font></p>
        <p><font >return TRUE;</font></p>
        <p><font >}</font></p>
          <p><font >  其中pos类型为POSITION,相当于链表的指针,指向链表当前元素。CStringList的成员函数GetHeadPosition()返回链表头指针。链表的GetNext()函数以当前指针为参数,返回下一个元素指针,同时修改pos,使它指向下一个元素。使用强制类型转换将GetNext()函数返回的元素指针转化为CString类型,然后调用CString::Empty()方法清除该行中的所有字符。通过一个while循环,清除所有文本行的数据。最后调用CStringList的RemoveAll()成员函数,清除链表中的所有指针(注意:此时这些指针指向的元素已经被清除)。</font></p>
        <div align="center"><center><table border="2"
        cellpadding="2" cellspacing="0" width="100%"
        bgcolor="#95DDFF">
            <tr>
                <td width="100%"><font >提示:应用程序对象的成员函数CWinApp::OnFileNew()在选择File菜单的New命令时被调用,缺省时在InitInstance()中也会被调用。原理是在InitInstance()中有一个命令行参数的执行过程,当命令行上没有参数时,函数ParseCommandLine(cmdInfo)会调用CCommandLineInfo

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -