Delphi文本编辑器的设计

来源:互联网 发布:好易网络电视tv版 编辑:程序博客网 时间:2024/04/28 01:44

本章介绍多文本界面(MDI)、多页面界面(MPI)技术;VCL库中TMemo,TEdit 控件以及有关文本编辑的常用对话框的使用。我们开发的MPIEdit.dpr是一个文本编辑的实用程序,可实现如下功能:

  ● 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;

  4.4.2查找对话框部件 

  查找对话框部件为应用程序提供查找对话框, 用户可使用查找对话框在文本文件中查找字符串。

  可用Execult方法显示查找对话框,如图4.8。应用程序要查找的字符放到FindText属性中。Options 属性可决定查找对话框中有哪些选项。例如, 用户可选择是否显示匹配检查框。Options的常用选项如表4.2所示。

  如果用户在对话框中输入字符并选择FindNext按钮,对话框将发生OnFind事件。 

  表4.2 查找对话框的Options属性的取值及含义

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

  取值           含义

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

  frDown 如果是真值,对话框中出现Down按钮,查找方向向下。如果是假值,Up按钮将被选中,查找方向向上,frDown 值可在设计或运行时设置。

  frDisableUpDown 如果是真值,Up和Down按钮将变灰,用户不能进行选取;如果是假值,用户可以选择其中之一。

  frFindNext 如果是真值,应用程序查找在FindNext属性中的字符串。

  frMatchCase 如果是真值,匹配检查框被选中。设计、运行时均可设置。

  frWholeWord 如果是真值,整字匹配检查框被选中,设计、运行时均可设置。

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

  在OnFind事件中可使用Options属性来决定以何种方式查找。Find方法响应查找对话框的OnFind事件。 

  procedure TEditform.Find(Sender: TObject);
begin
with Sender as TFindDialog do
if not SearchMemo(Memo1, FindText, Options) then
ShowMessage('Cannot find "' + FindText + '".');
end;

  其中SearchMemo函数是Search单元中定义的,SearchMemo可在TEdit,TMemo,以及其它TCustomEdit派生类中查找指定的字符串。查找从控件的脱字号(^)开始, 查找方式由Options决定。如果向后查找从控件的StlStart处开始,如果向前查找则从控件的SelEnd处查找。

  如果在控件中找到相匹配的字符串,则字符串被选中,函数返回真值。如无匹配的字符串,函数返回假值。

  特别注意的是TEdit,TMemo中有一个HideSeletion属性,它决定当焦点从该控制转移至其它控制时,被选中的字符是否保持被选中的状态。如果是真值,则只有获得焦点才能保持被选中状态。查找时,焦点在查找对话框上,因此要想了解查找情况,必须将HideSeletion设成假值。控制的缺省值为真值。

  SearchMemo代码如下: 

  unit Search;
interface
uses WinProcs, SysUtils, StdCtrls, Dialogs;
const
WordDelimiters: set of Char = [#0..#255] - ['a'..'z','A'..'Z','1'..'9','0']; 
function SearchMemo(Memo: TCustomEdit;
const SearchString: String;
Options: TFindOptions): Boolean; 
function SearchBuf(Buf: PChar; BufLen: Integer;
SelStart, SelLength: Integer;
SearchString: String;
Options: TFindOptions): PChar; 
implementation 
function SearchMemo(Memo: TCustomEdit;
const SearchString: String;
Options: TFindOptions): Boolean;
var
Buffer, P: PChar;
Size: Word;
begin
Result := False;
if (Length(SearchString) = 0) then Exit;
Size := Memo.GetTextLen;
if (Size = 0) then Exit;
Buffer := StrAlloc(Size + 1);
try
Memo.GetTextBuf(Buffer, Size + 1);
P := SearchBuf(Buffer, Size, Memo.SelStart,
Memo.SelLength,SearchString, Options);
if P <> nil then
begin
Memo.SelStart := P - Buffer;
Memo.SelLength := Length(SearchString);
Result := True;
end;
finally
StrDispose(Buffer);
end;
end; 
function SearchBuf(Buf: PChar; BufLen: Integer;
SelStart, SelLength: Integer;
SearchString: String;
Options: TFindOptions): PChar;
var
SearchCount, I: Integer;
C: Char;
Direction: Shortint;
CharMap: array [Char] of Char; 
function FindNextWordStart(var BufPtr: PChar): Boolean;
begin { (True XOR N) is equivalent to
(not N) }
Result := False; { (False XOR N) is equivalent
to (N) }
{ When Direction is forward (1), skip non
delimiters, then skip delimiters. }
{ When Direction is backward (-1), skip delims, then
skip non delims }
while (SearchCount > 0) and
((Direction = 1) xor (BufPtr^ in
WordDelimiters)) do
begin
Inc(BufPtr, Direction);
Dec(SearchCount);
end;
while (SearchCount > 0) and
((Direction = -1) xor (BufPtr^ in
WordDelimiters)) do
begin
Inc(BufPtr, Direction);
Dec(SearchCount);
end;
Result := SearchCount > 0;
if Direction = -1 then
begin { back up one char, to leave ptr on first non
delim }
Dec(BufPtr, Direction);
Inc(SearchCount);
end;
end; 
begin
Result := nil;
if BufLen <= 0 then Exit;
if frDown in Options then
begin
Direction := 1;
Inc(SelStart, SelLength); { start search past end of
selection }
SearchCount := BufLen - SelStart - Length(SearchString);
if SearchCount < 0 then Exit;
if Longint(SelStart) + SearchCount > BufLen then
Exit;
end
else
begin
Direction := -1;
Dec(SelStart, Length(SearchString));
SearchCount := SelStart;
end;
if (SelStart < 0) or (SelStart > BufLen) then Exit;
Result := @Buf[SelStart]; 
{ Using a Char map array is faster than calling
AnsiUpper on every character }
for C := Low(CharMap) to High(CharMap) do
CharMap[C] := C; 
if not (frMatchCase in Options) then
begin
AnsiUpperBuff(PChar(@CharMap), sizeof(CharMap));
AnsiUpperBuff(@SearchString[1],
Length(SearchString));
end; 
while SearchCount > 0 do
begin
if frWholeWord in Options then
if not FindNextWordStart(Result) then Break;
I := 0;
while (CharMap[Result[I]] = SearchString[I+1]) do
begin
Inc(I);
if I >= Length(SearchString) then
begin
if (not (frWholeWord in Options)) or
(SearchCount = 0) or
(Result[I] in WordDelimiters) then
Exit;
Break;
end;
end;
Inc(Result, Direction);
Dec(SearchCount);
end;
Result := nil;
end; 
end.

  4.4.3 替换对话框部件 

  替换对话框部件为应用程序提供替换对话框。如图4.9。它包括查找对话框的所有功能,此外还允许使用者更换被选中的字符串。FindText 属性是应用程序需查找的字符串。ReplaceText属性是被选中字符的替换字符串。Options 属性决定对话框的显示方式。其值如表4.3所示。

  与查找对话框一样,替换对话框亦有OnFind 事件。用户输入查找字符串并按FindNext按钮时,发生OnFind 事件。用户选择Replace 或ReplacAll 时, 对话框发生OnRelpace事件,要替换的字符串存入ReplaceText属性中,要编写相应的代码以支持替换功能。 

  表4.3 替换对话框的Options属性的取值及含义

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

  取值              含义

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

  frRelpace 如果是真值, 应用程序将ReplaceText 属性中的字符串替换FindText属性中的字符串。

  frReplacAll 如果是真值,应用程序将ReplaceText属性中的字符串替换,查找到的所有FindText属性中的字符串。

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

  例程中TEditForm.Replace方法响应OnReplace事件,Replace方法首先判断控制中被选中字符串是否与替换字符串相等,如果不等则进行替换。而后根据Options中的方式循环进行查找替换。直至无匹配字符串为止。其代码如下: 

  procedure TEditForm.Replace(Sender: TObject);
var
Found: Boolean;
begin
with ReplaceDialog1 do
begin
if AnsiCompareText(Memo1.SelText, FindText) = 0 then
Memo1.SelText := ReplaceText;
Found := SearchMemo(Memo1, FindText, Options);
while Found and (frReplaceAll in Options) do
begin
Memo1.SelText := ReplaceText;
Found := SearchMemo(Memo1, FindText, Options);
end;
if (not Found) and (frReplace in Options) then
ShowMessage('Cannot find "' + FindText + '".');
end;
end;

  4.4.4 打开对话框部件 

  打开对话框部件为应用程序显示打开对话框。使用Execute方法可显示打开对话框用户通过选择文件类型下拉框中的文件类型,可以确定显示在文件列表中的文件。 例如,如果用户选择*.txt文件类型,那么只有在当前目录下的文本文件才会显示在文件列表中。文件扩展名通常也称为过滤器。

  打开对话框包含一个Filters(过滤器)的属性,它可确定文件类型和在文件类型下拉框中的顺序。应用程序可以为打开对话框定义多个过滤器,对话框的FilterIndex 属性可以决定哪个过滤器是文件类型下拉框中的缺省过滤器。如FilterIndex等于2,表示程序运行时出现在文件类型下拉框的过滤器是第2个过滤器。

  例程中关于文件打开的代码如下: 

  procedure TEditForm.Open/Click(Sender : TObject);
begin
if OpenDialog/.Execult then
begin

Open(Open Dialog/.FileName)
end
end;

  打开,保存对话框中的Options属性值见表4.4 

  表4.4 打开、保存对话框的Options属性取值及含义

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

  值               含义

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

  ofAllowMultiSelect 如果是真值,则允许在文件名列表中选择多个文件。

  ofCreatePrompt 如果是真值,当用户在文件编辑框中输入一不存在的文件名,

  并选择OK按钮,则会出现消息框, 提示用户此文件不存在并询问是否以此文件名创建一新文件。

  ofExiengronDifferent 如果是真值,从对话框中返回的文件扩展名将不同于缺省扩展名。

  其值存入DefaultExt属性中。

  ofFileMustExist   如果是真值, 当用户在文件编辑框中输入一个不存在的文件名时,并选择OK按钮, 则会出现一消息框提示用户此文件不存,并询

  问是否输入了正确的路径和文件名。

  ofNoChangeDir 如果是真值,当前目录将设置成对话框第一次出现的目录,并忽略任何目录改变。

  ofOverWritePrompt 如果是真值,当用户试图保存一个已存在的文件时, 将出现一消息框,提示用户此文件已存在,并询问是否覆盖。

  ofPathMastExit 如果是真值,用户在文件名编辑框只能输入有效路径名, 否则出现消息框,提示用户路径无效。

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

  表4.4 打开、保存对话框中的Options属性取值及含义

  文件保存对话框与打开对话框类似,如图4.11。它的Option属性见上表。例程在保存文件前先对文件进行读写判断,如果文件是只读文件或未指定文件名的新文件, 则程序对文件不保存,否则备份文件。代码如下:

  procedure TEditForm.Save1Click(Sender: TObject);
procedure CreateBackup(const Filename: string);
var
BackupFilename: string;
begin
BackupFilename := ChangeFileExt(Filename, BackupExt);
DeleteFile(BackupFilename);
RenameFile(Filename, BackupFilename);
end; 
function IsReadOnly(const Filename: string): Boolean;
begin
Result := Boolean(FileGetAttr(Filename) and faReadOnly);
if Result then MessageDlg(Format('%s is read only.',
[ExtractFilename(Filename)]), mtWarning, [mbOK], 0);
end; 
begin
if (Filename = '') or IsReadOnly(Filename) then
SaveAs1Click(Sender)
else
begin
CreateBackup(Filename);
Memo1.Lines.SaveToFile(Filename);
Memo1.Modified := False;
end;
end;

  其中CreateBackup过程用以改变需备份文件的扩展名。IsReadOnly 用以判断文件属性。 

  4.5 文件打印 

  在Delphi中,文件打印有两种方式:

  1. 将文件变量分配给打印机,用此变量名创建或打开文件后, 往此文件变量写入的任何文本都视为向打印机输出,以下过程可实现文件的打印。 

  procedure TEditForm,Print1Click(Sender: TObject);
var
Line: Integer;
PrintText: System.Text;
begin
if PrintDialog1.Execute then
begin
AssignPrn(PrintText)
Rewrite(PrintText);
Print.CanvasFont := Memo1.Font;
For Line := 0 to Memo1.Lines.Count - 1 do
Writeln(PrintText,Memo1.Line[line];
System.Close(PrintText);
end;
end;
 

  2. 利用Printers单元中定义的TPrinter对象进行文件打印,本章例程采用这种方法打印文件。 

  4.5.1 TPrinter对象 

  TPrinter对象可调用Windows的打印机,在Printer 单元中定义了TPrinter 的实例Printer,用户可直接使用。

  调用TPrinter的BeginDoc方法可开始一项打印工作,调用EndDoc 方法可结束一项已成功发送给打印机的工作。如果在发送过程中出现问题或用户想中途终止打印工作,可调用Abort方法。

  通过检查Printing属性可测试当前是否有打印工作,如果打印工作被终止,Abort属性为真。

  Canvas属性代表打印表面,Brush,Font,Pen属性可决定打印字体或图像的特征。

  Printers属性中包含着已安装的打印机列表,PrinterIndex 属性是当前选择的打印

  机,Fonts属性中有当前打印机支持的字体。Orientertion属性可决定打印方向。

  PageHeight,PageWith中包含着当前的高度和宽度。PageNanber为当前页的值。

  设置Title属性可决定在Windows打印管理器或网络中出现的文本。 

  4.5.2 TPrintDialog打印对话框 

  TPrintDialog部件显示一打印对话框。用户在对话框中,可以选择打印机、打印页数、打印份数。当用户选择对话框中的Setup按钮,则出现打印设置对话框。

  调用Execute方法显示打印对话框。如图4.12。使用Option属性可设置打印对话框显示的形式。Options的设置如表4.5所示。

  PrintRange属性可定义打印的范围。如果PrintPage的值是prPageNums,则可以设置FromPage和ToPage属性来确定打印范围。设置MinPage,MaxPage属性可限制用户的打印范围。 

  表4.5 打印对话框的Option属性的取值及含义

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

  取值              含义

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

  PoHelp 如果是真值,对话框出现帮助按钮。

  PoPageNums 如果是真值,页数按钮有效,用户可以设置打印范围。

  PoPrintToFile 如果是真值,文件打印检查框将出现在对话框中,用户可以选择文件打印。

  PoSelection 如果是真值,选择按钮有效, 用户可打印文件中所选择的文本。

  PoWarning 如果是真值,在打印机尚未安装时,用户选择OK 按按钮将出现警告信息。

  PoDisablePrinttoToFile 如果是真值,而PoPrintToFile亦是真值时,当对话框出现时,文件打印对话框将无效。

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

  本章例程是利用Printer的画布进行文本打印的。用户选择打印菜单后,将弹出打印对话框,用户可设置各种参数。当用户选择打印按钮后,打印工作进行发送,此时将弹出打印取消对话框,见图4.13, 用户可中止打印工作。有关打印和打印取消的代码如下:  

  procedure TEditForm.Print1Click(Sender: TObject);
var
DistanceLine,Line: Integer;
PrintText: System.Text;
begin
if PrintDialog1.Execute then
begin
Printer.Canvas.font := Memo1.Font;
DistanceLine := Trunc(1.5*FontDialog1.font.size);
OpenPrintCancelDialog;
Printer.BeginDoc;
for line := 0 to Memo1.Lines.Count - 1 do
begin
Printer.canvas.textout(0,DistanceLine*Line,Memo1.lines[Line]);
end;
Printer.EndDoc;
BtnBottomDlg.free;
end;
end;
procedure TEditForm.OpenPrintCancelDialog;
begin
BtnBottomDlg := TBtnBottomDlg.Create(Application);
BtnBottomDlg.show;
BtnBottomDlg.canvas.Brush.Color := clActiveBorder;
BtnBottomDlg.canvas.TextOut(50,20,'Print'+FileName);
BtnBottomDlg.canvas.TextOut(30,40,'if you want to
stop, please choice Cancel Button.');
end;

 

程序员特区

http://coderarea.net/html/bianchengyuyan/DELPHIyuyan/DELPHIzonghe/2009/0506/93055.html

原创粉丝点击