DELPHI基础教程

第四章 文本编辑器的设计(一)

  ● MDI 的编辑环境

 ● MPI 的编辑环境

 ● 创建打开、编辑、保存文件

 ● 查找、替换文件中指定的字符串

 ● 复制、粘贴、剪切字符串

 ● 设置文件字体大小

 ● 打印文件 

 本章将通过 MPIEdit 实用程序逐一介绍在 Delphi 中如何实现上述功能。

  文本编辑器是一种常用的应用程序。用户在编辑器中编辑多种文件,在多个文件之间进行数据交换,对文件进行各种属性设置,并按自己要求打印文件。 

4.1 多文本界面 

  多文本界面是一种在一个应用程序中同时打开两个或更多文件的界面形式。例如在字处理程序可同时打开多个文件,用户可在多个文件中方便地进行切换.

MDI 应用程序提供了一种方便的方式,使得用户在同一工作区域内对多个文档进行观察和交换数据。 MDI 工作区域可分为父窗体和子窗体,在 Dephi 的 MDI 应用程序中,父窗体通常是程序的主窗体。

 在 MDI 中,父窗体之外的窗体称为子窗体,文档或其它数据在子窗体打开。这些文档可以是相同的文件格式,或在应用程序支持下也可以是不同的文件格式。

 在设计阶段,可创建 MDI 父窗体作为应用程序主窗体, 亦可创建子窗体样板。 Delphi 允许创建多个子窗体类型,但 MDI 应用程序只支持其中的一种。

  本节讲述创建 MDI 应用程序的基本步骤:

 ● 创建主窗口

 ● 创建子窗口

 ● 创建主窗口菜单

 ● 融合菜单

 ● 运行时创建子窗口 

4.1.1 创建父窗口 

 在 MDI 应用程序中,主窗口为应用文档提供一个工作区域。这个区域可打开一个或多个子窗口,创建父窗口是建立 MDI 应用程序的第一步。

  创建父窗口与其它窗口类似,不同之处在于设置窗体的 FormStyle 属性。

   FormStyle 属性可决定一个窗体是父窗口还是子窗口,或不是 MDI 类型。 只能在设计阶段确定 FormStyle 。在 Object Inspector 窗口中将 FormStyle 属性设置成 fsMDIForm 。值得注意的是应当把父窗口定义为应用程序的主窗体,否则程序编译会出错。 

4.1.2 创建子窗口 

 设计阶段可创建子窗口的样板,用户在运行进使用样板的实例。子窗口是缺省可见的,如果应用程序在运行进创建子窗口,不要让 Delphi 自动地创建。

  创建子窗口时将窗体的 FormStyle 属性设置为 fsMDIChild 。如果程序在运行时创建子窗口,则

   1. 选择 OPtions|Project 菜单,系统弹出自动创建列表对话框;

   2. 在自动创建列表中选中子窗口;

  3. 单击 > 按钮将子窗口移至可得到 (Available) 窗体列表;

   4. 并单击 OK 按钮退出。

4.1.3 创建应用程序菜单与菜单融合 

 父窗口的菜单应作为应用程序主菜单。如果子窗口有菜单, 则当子窗口在运行获得焦点并最大化时,子窗口的菜单项将融合父窗口菜单。

  创建父窗口与子窗口菜单的方法与创建普通窗体菜单类似, 详细步骤见第一章。菜单融合是指程序运行过程中,子菜单与父窗口菜单的相互作用。 如当子窗口获得焦点时,子窗口的菜单或插入主窗口的菜单中,或将替换部分或全部的父窗口菜单。

  进行菜单融合需设置的两个属性:

  ● 窗体的 Menu 属性

  ● 菜单项的 GroupIndex 属性

       Menu属性定义窗体的活动菜单,而菜单融合只对活动菜单进行。 如果窗体有多个菜单部件,运行时可通过以下代码进行改变:

  Form1.Menu := SecondMenu; 

        GroupIndex属性决定出现在菜单条中各菜单项的位置,在菜单融合中,GroupIndex 将

决定融合菜单是插入还是替换主窗体菜单条中的菜单。

  GroupIndex的缺省值是0,可以用下规则确定其值:

  1. 数值越小,菜单的位置越靠左。

  例如:GroupIndex为0的菜单将出现在菜单条中的最左端。随着GroupIndex数值的增大,菜单项依次向右排列。

  2. 若需替换主菜单中的某一菜单项,则将子菜单相应菜单项的GroupIndex设为与之相等的值。这条规则适合一个或多个菜单项。例如,主菜单中的"Edit"菜单项的GroupIndex 的值为1。将子菜单的一个或多个菜单项的GroupIndext的值设为1,则在运行时,这些菜单项替换主窗口的"Edit"菜单。

  将同一窗体的多个菜单项的GroupIndex设为相同值, 原有的排列顺序在菜单融合时将保持

不变。

  3. 若要在菜单融合时插入菜单项,需在主菜单中预留数值“位置”。例如,主菜单的两菜单项数值为0,5,则子菜单GroupIndex数值为1,2,3,4的菜单在融合时将插入其中。

  在使用MDI界面时,用户通常会打开多个窗体。为了使用户方便地进行窗体切换,常设有一个进行切换的菜单项.此菜单列出了打开窗体的名称,当用户选择其中的一个时,程序进行相应的窗体切换。在 Delphi 的 MDI 设计时,可非常方便地实现这一功能。方法是将父窗口的 WindowMenu 设置成该菜单项的名字即可。

4.2 多页面界面 

 多页面界面是一种非常友好的界面形式。它由一个窗体和多个页面组成, 关于每个页面的信息列在窗体底部的标签 (Tabs) 上,用户可通过选择标签来进行页面切换。 每次只有一个页面显示在窗体中。 MPI 较 MDI 使用更为方便,且切换速度更快。本章例程就是多页面界面的例子。另外 Delphi 集成开发环境中的代码编辑 (Code Editor) 窗体是 MPI 应用在文本编辑中的实例。在 MPI 中,一个窗体内的多个文件可以方便地进行切换和交换数据。

       多页面界面分为静态MPI 和动态 MPI 两种形式。静态 MPI 的标签数量固定,用户在事先设计好的多个页面上进行切换。象选择对话框 (Option Dialog) 就属于静 MPI 。动态 MPI 的标签数量不固定,由程序根据需要动态的产生或消除,象代码编辑窗体就是动态 MPI ,程序可根据用户的需要产生多个文本页面,也可以动态地关闭页面。利用 Delphi 的 TNotebook 和 Ttabset 可十分方便地设计静态 MPI 。设计动态 MPI 则需要编写专门的代码。

4.2.1 静态多页面界面 

  TNotebook,TTabSet 可用来开发静态多页面界面。 TNotebook 部件能显示多页, 每页都有相应的控制。通常 TNotebook 与 TTabset 配合进行控制。 TTabset 有一组水平的标签 , 每个标签可通过创建字符串列表进行某种控制。

   MPIEDit 例程中的主窗体中有一个 TNotebook 部件和 TTabSet 部件。 把两个部件的 Aglin 属性设置成 bsTop 和 bsBotton ,使它们分别处在窗体的上下两部分。为了使 TTabSet 与 TNotebook 配合工作,使用下代码: 

  TabSet1.Tabs := Notebook1.Page; 

       另外,在 TabSet 的 OnClick 事件中定义下如下代码,可使用户在选择标签时开打相应的页。 

  procedure TEditForm.TabSetClick(Sender : TObject); …

   begin

Notebook1.PageIndex := TabSet1.TabIndex;

end; 

        设计静态 MPI 时,可在部件窗体 (Component Palette) 的 WIN3 。 1 页面中选中 TNotebook 部件,然后在 Object inspector 窗体中双击 TNotebook 的 Pages 属性, Dephi 将弹出对话框,用户可以在此确定 Notebook 的页数和字符串列表,如图 4.6 。关闭对话框后, 可对每一页进行设计,使用鼠标右按钮弹出快速菜单进行页面切换。

4.2.2 动态多页面界面 

 使用 Delphi 进行静态 MPI 设计非常简单,进行动态 MPI 设计则需编写专门的代码。 对

于一个多页面文本编辑器,应能实现以下功能:

  ● 动态生成页面,每个页面均能进行文本编辑

 ● 动态关闭页面,直到窗体中只有一个页面为止

 ● 页面切换不影响各种文本编辑操作 

 为了实现以上功能,程序中使用了动态页面类 (TDynaPage) ,其定义如下: 

   type TDynaPage = Class(TObject); 

该类可根据需要动态的产生页面, 每个页面上创建了可进行文本编辑的 TMeno 部件。 

   procedure...

  puclic

CurPage : integer;

FileList : TSringList;

end; 

        CurPage 表示当前用户选择的页面数,用户切换、增加、删除页面均影响 CurPage 的值, CurPage 初如化为零页。 FileList 存放打开或创建文件的名字以及与这些文件相关的编辑部件 TMemo ,页面动态创建、删除将影响 FilstList 的值。

   TNotebook 部件创建后至少有一个页面,因此 Pages 属性不是空值,只要往 Pages 中加入字符串, Delphi 自动地把该字符串与 TPage 类对象相联系。 TPage 类是 TCustomEdit 派生出来的,在对象浏览器 (Object Browse) 中可观察到 TPage 的数据成员和方法。静态生成的页面也是 TPage 类。

  要创建多页面编辑器,必须从 TPage 的父件 (Parent 属件 ) 创建相应编辑部件。但在动态创建页面时, TPage 只是一个与字符串相联系的 TObject 类,不能写成: 

   MemoParent := Notebook1.Pages.Object[ ]; 

在 Delphi 中,宣称对象和创建对象都是用指针来标识, 因此可用无类型指针进行指针传递。 

  var

Pi : Pointer;

begin

Pi := Notebook1.Pages.Object[];

Memo.Parent := Pi;

end; 

这样就可在 TPage 上动态创建编辑部件了。

  往 Notebook1 中动态生成页面时,页面应所相应的切换, TDynaPage. Notebook1.Tabset1 有关的属性要作相应的调整。

            TDynaPage 的 DynaAdd 方法定义如下: 

procedure TDynaPage.DynaAdd(Sender:TNotebook;FileName:String);

var

Pi:Pointer;

Memo:TMemo;

begin

Sender.Pages.add(FileName);

Pi:= Sender.Pages.Objects[Sender.Pages.Count-1];

DynaMemo(pi);

DynaPage.FileList.addObject(FileName,Memo1);

EditForm.TabSet1.Tabs := Sender.Pages;

EditForm.Tabset1.TabIndex:=Sender.Pages.Count-1;

EditForm.Notebook1.PageIndex := EditForm.Tabset1.TabIndex;

DynaPage.CurPage:= Sender.Pages.Count-1;

end; 

procedure DynaMemo(Pi:Pointer);

var

Memo:TMemo;

begin

Memo:=TMemo.Create(Pi);

Memo.Parent:=Pi;

Memo.Align:=alClient;

Memo.borderStyle:=bsNone;

Memo.HideSelection:=False;

Memo1:=Memo;

end;

procedure TDynaPage.Del(Sender:TNotebook;No:integer);

var

Pi:pointer;

begin

Sender.Pages.delete(No);

EditForm.TabSet1.Tabs.delete(No);

Filelist.Delete(No);

DynaPage.CurPage:=EditForm.TabSet1.TabIndex;

Sender.PageIndex := EditForm.Tabset1.TabIndex;

Pi:=FileList.Objects[DynaPage.CurPage];

Memo1:=Pi;

EditForm.Caption:=Sender.Pages.Strings[DynaPage.CurPage];

end;

  当用户在多个页面中进行切换时,程序应当保证对当前页面进行编辑。 例如在多页编辑器中,用户选中某一页面,即可对该页面中的文件进行编辑、寻找、设置、打印等。为了实现这一功能,定义了一个 TMemo 类型的变量: Memo1, 该变量没有实例化,每次调用 DynaAdd,DynaDel 方法均定把 TabIndex 指定页面的 Memo 指针传给 Memo1 。这样在程序运行中,始终有一个实例化的 Memo 指针赋给 Memo1 ,而菜单中的文本编辑功能均对 Memo1 进行操作。这种指针传递就能保证对当前页进行操作。

  定义了 TDynaPage 后,只需在 Open,Close 菜单项中加入如下代码,即可方便的在用户打开关闭文件时创建成删除页面。 

  procedure TEditForm.Close1Click(Sender: TObject);

begin

if DynaPage.CurPage<>0 then

DynaPage.Del(Notebook1,DynaPage.CurPage);

if Notebook1.Pages.count = 1 then

Close1.Enabled:=False;

end; 

procedure TEditForm.Open1Click(Sender: TObject);

begin

if OpenDialog1.Execute then

begin

if not(OpenFile or NewFile) then

begin

OpenFile:=true;

Open(OpenDialog1.FileName);

Notebook1.Pages.Strings[0]:=ExtractFileName( OpenDialog1.FileName);

TabSet1.Tabs:=Notebook1.Pages;

end

else

begin

DynaPage.DynaAdd( Notebook1, ExtractFileName(OpenDialog1.FileName));

Open(OpenDialog1.Filename);

if Notebook1.Pages.count > 1 then

Close1.Enabled:=True;

end;

end;

end; 

4.3 文本编辑部件及应用 

4.3.1 TEdit 部件 

        TEdit 部件是一个标准的编辑框,用户可在编辑框中输入数据。编辑框也可向用户显示数据。编辑时只能读写一行信息。

  TEdit 的 Text 属性存放着用户输入的数据或向用户显示的数据, Modified 属性用以标识 Text 的数据是否改变,可通过设置 Maxlength 属性值来限制用户输入字符的个数量, CharCase

          属性可定义编辑框中字符的大小写。如果设计者想禁止用户输入,可将 ReadOnly 属性设置成真值。编辑框也能用做密码输入框。通过设置 PassWordChar 属性的值,可将用户输入的字符在编辑框中显示成指定的字符,如 "*" 号等。编辑框还可以进行字符选择操作、粘贴、复制和剪切操作。 

4.3.2 TMemo 部件 

       TMemo 部件与 TEdit 部件类似,能向用户显示数据,用户也可输入数据。与 TEdit 部件

不同的是, TMemo 部件可以处理多行文本,因此主要用于编辑文件。

  TMemo 的 Text 属性只能在运行时才能访问。 Modified 属性用以标识 Text 的数据是否改

变,通过设置 MaxLength 属性值来限制用户输入字符的数量。

 如果把文本当成一个整体进行访问,可使用 Text 属性;若想逐行访问,则要使用 Lines 属性。 Lines 属性能对文件更方便地进行访问。 Lines 是 TStrings 类型的,因此可使用 Add 、 Delete 方法,例如在 Memo1 中加入一行字符串的代码如下:  

Memo1.Lines.Add('Another line is added'); 

        通过 Lines 属性可以方便地把文件读入部件中,例程中使用下面的代码将文件读入 Memo1 : 

Memo1.Lines.loadFromFile(Filename). 

从 TMemo 部件中剪切、复制、粘贴文本非常方便,只需使用 CutToclipboard , CopyToClipBroad,PasteFromClipBoard 方法,其代码如下: 

   Memo1.CopyToClipboard

   Memo1.CutToClipboard

   Memo1.PasteFromClipboard 

        TMemo 有一些属性,用以控制文本的显示效果。 ScrollBars 属性可以定义部件的水平滚动条和垂直滚动条。当文件字体改变时,使用 AutoSize 属性可使部件大小做相应的调整。设置 WordWrap 属性可以实现自动换行。

  例程中 Edit|WordWrap 菜单项提供了设置 WordWrap 的功能,并可根据 WordWrap 的值决定滚动条的形式。当 WordWrap 为真时,不需要水平滚动条, 并在菜单中作出检查记号。

其代码如下: 

procedure TEditForm.SetWordWrap(Sender: TObject);

begin

with Memo1 do

begin

WordWrap := not WordWrap;

if WordWrap then

ScrollBars := ssVertical else

ScrollBars := ssBoth;

WordWrap1.Checked := WordWrap;

end;

SetEditRect;

end; 

        TMemo 部件提供了一组关于选择文本的属性和方法。如果想在部件成为当前控件时自动选择文本,可设置 AutoSelect 属性。运行时可用 SelectAll 方法选中部件的全部文本。 Selstart 属性返回选中文本的开始位置, SelText 包含着被选中的文本。 SelLength 属性返回选中文本的长度,这两个属性可用于字符串的查找和替换。下一节将详细讨论。

   TMemo 的 Modified 属性是一个运行时才能得到的属性,可判断部件被创建时或 Modified 属性最后一次设置成假值之后,部件上的文本是否修改。如果修改了, Modified 将设成真值,反之假值。

 例程中在关闭文件时将测试文件的 modified 属性,如果文件修改后尚未保存, 将出现对话框,询问用户是否保存文件,其代码如下: 

  procedure TEditForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

var

DialogValue: Integer;

FName: string;

begin

if Memo1.Modified then

begin

FName := Caption;

DialogValue := MessageDlg(Format(SWarningText, [FName]), mtConfirmation,

[mbYes, mbNo, mbCancel], 0);

case DialogValue of

id_Yes: Save1Click(Self);

id_Cancel: CanClose := False;

end;

end;

end; 

4.4 常用对话框的使用 

Delphi 的可视部件类库 (Vistual Component Liberty) 中,有一组对话框部件,在对象选择板的 Dialog 页面中可以找到。 本节着重介绍与文件编辑有关的字体对话框 (TFontDialog Componement) ,查找对话框 (TFindDialog Componement) ,替换对话框 (TReplace Dialog Componement) ,文件对开对话框 (TOpenDIalog Componement).

   应用这几个对话框可对文件进行字体设置、查找、替换等操作,但需要编写相应的代码。 

4.4.1 字体对话框部件 

 字体对话框部件在应用程序中产生字体对话框, 用户可在对话框中进行字体选择和属性设置。用户选择字体并按下 OK 按钮之后,有关信息便贮存在部件的 Font 属性中。

  应用程序可通过调用字体对话框的 Execult 方法来显示对话框,当用户选择 OK 按钮时, Execult 返回 True 值,否则返回 Flase 值。

应用程序可以使用 Options 属性来定义字体对话框的显示和行为方式:例如可在对话框中定义一个帮助按钮或指定出现在字体列表框中的字体。有关 Options 的主要取值如下表 4.1 : 

表 4.1 字体对话框的 Options 取值及含义

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

取值           含义

───────────────────────────────────────

AdAnsiOnly 如果是真值,只能使用 Window 字符集,

fdEffects 如果是真值,对话框中显示颜色列表和效果检查框;用户可使

用效果检查框定义 Strikout 下划线文本;使用颜色列表定义字体

颜色。

fdForceFontExise  如果是真值,用户在字体组合框中输入字体名后选择 OK 按钮,

将出现一个用户字体无效的消息框。

fdNoOEMFont     如果是真值,字体组合框中将不显示向量字体。

fdShowHelp 如果是真值,对话框显示 Help 按按钮。

fdWysiwyg 如果是真值, 只有打印和屏幕均可得到的字体才会出现在字体

            组合框中。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 

  例程中 (Edit/Font) 菜单具有设置文本字体的功能,其代码如下:

 

   procedure TEditForm.SetFont(Sender : TObject);

begin

FontDialog.Font := Memo1.Font;

if FontDialog1.Execult then

Memo1.Fout := FontDialog1.Font;

SetEdit Rect;

end;  

[目录] [上一页] [下一页]