後一頁
前一頁
回目錄
回首頁
第四章 文本編輯器的設計(一)

  本章介紹多文本介面(MDI)、多頁面介面(MPI)技術;VCL庫中TMemoTEdit 控件以及有關文本編輯的常用對話方塊的使用。我們開發的MPIEdit.dpr是一個文本編輯的公用程式,可實現如下功能:

  ● MDI的編輯環境

  ● MPI的編輯環境

  ● 建立打開、編輯、存檔文件

  ● 找到、覆寫文件中指定的字元串

  ● 復制、貼上去、剪下來字元串

  ● 設定文件字形大小

  ● 列印文件 

  本章將通過MPIEdit公用程式逐一介紹在Delphi中如何實現上述功能。

  文本編輯器是一種常用的應用程式。用戶在編輯器中編輯多種文件,在多個文件之間進行數據交換,對文件進行各種屬性設定,並按自己要求列印文件。 

4.1 多文本介面 

  多文本介面是一種在一個應用程式中同時打開兩個或更多文件的介面形式。例如在字處理程式可同時打開多個文件,用戶可在多個文件中方便地進行切換.

MDI應用程式提供了一種方便的方式,使得用戶在同一工作區欄位內對多個文檔進行觀察和交換數據。MDI工作區欄位可分為父窗體和子窗體,在DephiMDI應用程式中,父窗體通常是程式的主窗體。

  在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介面時,用戶通常會打開多個窗體。為了使用戶方便地進行窗體切換,常設有一個進行切換的選擇表項.此選擇表列出了打開窗體的標簽,當用戶選擇其中的一個時,程式進行相應的窗體切換。在DelphiMDI設計時,可非常方便地實現這一功能。方法是將父視窗的WindowMenu設定成該選擇表項的名字即可。

4.2 多頁面介面 

  多頁面介面是一種非常友好的介面形式。它由一個窗體和多個頁面群群組成, 關於每個頁面的資訊列在窗體底部的標籤(Tabs)上,用戶可通過選擇標籤來進行頁面切換。 每次只有一個頁面顯示在窗體中。MPIMDI使用更為方便,且切換速度更快。本章例程就是多頁面介面的例子。另外Delphi整合開發環境中的代碼編輯(Code Editor)窗體是MPI應用在文本編輯中的實例。在MPI中,一個窗體內的多個文件可以方便地進行切換和交換數據。

       多頁面介面分為靜態MPI和動態MPI兩種形式。靜態MPI的標籤數量固定,用戶在事先設計好的多個頁面上進行切換。象選擇對話方塊(Option Dialog)就屬於靜MPI。動態MPI的標籤數量不固定,由程式根據需要動態的產生或消除,象代碼編輯窗體就是動態MPI,程式可根據用戶的需要產生多個文本頁面,也可以動態地關閉頁面。利用DelphiTNotebookTtabset 可十分方便地設計靜態MPI。設計動態MPI則需要編寫專門的代碼。

4.2.1 靜態多頁面介面 

  TNotebook,TTabSet可用來開發靜態多頁面介面。TNotebook部件能顯示多頁, 每頁都有相應的控制。通常TNotebookTTabset配合進行控制。TTabset 有一群群組水準的標籤,每個標籤可通過建立字元串清單進行某種控制。

  MPIEDit例程中的主窗體中有一個TNotebook 部件和 TTabSet 部件。 把兩個部件的Aglin屬性設定成bsTopbsBotton,使它們分別處在窗體的上下兩部分。為了使TTabSetTNotebook配合工作,使用下代碼: 

  TabSet1.Tabs := Notebook1.Page; 

       另外,在TabSetOnClick事件中定義下如下代碼,可使用戶在選擇標籤時開打相應的頁。 

  procedure TEditForm.TabSetClick(Sender : TObject);

  begin

Notebook1.PageIndex := TabSet1.TabIndex;

end; 

        設計靜態MPI時,可在部件窗體(Component Palette)WIN31頁面中選中TNotebook 部件,然後在Object inspector窗體中連續按兩下TNotebookPages屬性,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有關的屬性要作相應的調整。

            TDynaPageDynaAdd方法定義如下: 

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部件是一個標準的編輯框,用戶可在編輯框中輸入數據。編輯框也可向用戶顯示數據。編輯時只能讀寫一行資訊。

   TEditText屬性存放著用戶輸入的數據或向用戶顯示的數據,Modified屬性用以標識 Text的數據是否改變,可通過設定Maxlength屬性值來限制用戶輸入字元的個數量,CharCase

          屬性可定義編輯框中字元的大小寫。如果設計者想禁止用戶輸入,可將ReadOnly屬性設定成真值。編輯框也能用做密碼輸入框。通過設定PassWordChar 屬性的值,可將用戶輸入的字元在編輯框中顯示成指定的字元,如"*"號等。編輯框還可以進行字元選擇操作、貼上去、復制和剪下來操作。 

4.3.2 TMemo 部件 

       TMemo部件與TEdit部件類似,能向用戶顯示數據,用戶也可輸入數據。與TEdit 部件

不同的是,TMemo部件可以處理多行文本,因此主要用於編輯文件。

  TMemoText屬性只能在執行時才能存取。Modified屬性用以標識Text的數據是否改

變,通過設定MaxLength屬性值來限制用戶輸入字元的數量。

  如果把文本當成一個整體進行存取,可使用Text屬性;若想逐行存取,則要使用Lines屬性。Lines屬性能對文件更方便地進行存取。LinesTStrings型式的,因此可使用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屬性返回選中文本的長度,這兩個屬性可用於字元串的找到和覆寫。下一節將詳細討論。

  TMemoModified屬性是一個執行時才能得到的屬性,可判斷部件被建立時或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; 


後一頁
前一頁
回目錄
回首頁