全國(guó)計(jì)算機(jī)等級(jí)考試二級(jí)Delphi輔導(dǎo)講義 220

字號(hào):

20.3.1.5 動(dòng)態(tài)DFM文件應(yīng)用揭秘
    1. 動(dòng)態(tài)DFM文件概述
    動(dòng)態(tài)DFM文件是相對(duì)于靜態(tài)DFM文件而言。所謂靜態(tài)DFM文件是指在Delphi開(kāi)發(fā)環(huán)境中設(shè)計(jì)的窗體文件。窗體的設(shè)計(jì)過(guò)程就是程序的編制過(guò)程。因此,動(dòng)態(tài)DFM文件就是指在程序運(yùn)行過(guò)程生成或存取的DFM文件。
    動(dòng)態(tài)DFM文件的創(chuàng)建和使用分別如下兩種情況:
    ● 在程序運(yùn)行過(guò)程中,由Create方法動(dòng)態(tài)生成窗體或部件,然后動(dòng)態(tài)生成其它部件插入其中生成DFM文件
    ● 在Delphi開(kāi)發(fā)環(huán)境中,設(shè)計(jì)生成DFM文件,然后用DFM 文件存取函數(shù),或者用Stream對(duì)象和Filer對(duì)象的方法,將DFM文件讀入內(nèi)存,進(jìn)行處理,最后又存入磁盤(pán)中
    由Delphi的窗體設(shè)計(jì)的常規(guī)方法生成的DFM文件在程序運(yùn)行一開(kāi)始就規(guī)定了部件的結(jié)構(gòu)。因?yàn)樵诖绑w設(shè)計(jì)過(guò)程中,窗體中的每個(gè)部件都在程序的對(duì)象聲明中定義了部件變量。這種固定的結(jié)構(gòu)雖然能方便應(yīng)用,但以犧牲靈活性為代價(jià)。
    在Delphi應(yīng)用程序中有時(shí)需要在運(yùn)行過(guò)程中創(chuàng)建控制,然后將該控制插入另一個(gè)部件中。例如:
    procedure TForm1.Button1Click(Sender: Tobject);
    var
    Ctrl: TControl
    begin
    Ctrl := TEdit.Create(Self);
    Ctrl.Top := 100;
    Ctrl.Left := 100;
    Ctrl.Width := 150;
    Ctrl.Height := 20;
    InsertControl(Ctrl);
    end;
    動(dòng)態(tài)插入控制的優(yōu)點(diǎn)是可以在任何時(shí)刻、任意位置插入任意數(shù)量的任何類(lèi)型的控制。因?yàn)閼?yīng)用程序需求在很多情況下是在程序運(yùn)行中才知道的,所以動(dòng)態(tài)插入控制就顯得很重要。而且在很多情況下,需要保存這些界面元素,留待程序再次調(diào)用。例如應(yīng)用程序界面的定制、系統(tǒng)狀態(tài)的保存、對(duì)話(huà)框的保存等。這時(shí)生成動(dòng)態(tài)DFM文件是選擇。
    動(dòng)態(tài)插入控制的不足之處是在插入控制前,無(wú)法直觀地看到控制的大小、風(fēng)格、位置等,也就是動(dòng)態(tài)插入控制的過(guò)程是非可視化的。但可以借助于靜態(tài)DFM文件的可視化設(shè)計(jì)。這就是生成和使用動(dòng)態(tài)DFM文件的第二種方法。也就是在應(yīng)用程序運(yùn)行前,在Delphi開(kāi)發(fā)環(huán)境中,使用可視化開(kāi)發(fā)工具設(shè)計(jì)所需窗口或部件的樣式,以DFM文件保存。然后在應(yīng)用程序運(yùn)行過(guò)程中,將DFM文件讀入內(nèi)存。Delphi的Stream對(duì)象和Filer對(duì)象在讀取DFM文件時(shí),會(huì)根據(jù)DFM文件的內(nèi)容自動(dòng)創(chuàng)建部件及其擁有的所有部件。
    在使用動(dòng)態(tài)DFM文件時(shí)有兩點(diǎn)需要注意。
    ● 每一個(gè)動(dòng)態(tài)插入的控制或部件必須在程序中調(diào)用RegisterClass進(jìn)行注冊(cè)
    ● 讀入DFM文件自動(dòng)創(chuàng)建部件后,如果調(diào)用了InsertControl方法, 則在關(guān)閉窗口時(shí)要調(diào)用RemoveControl方法移去該控制,否則會(huì)產(chǎn)生異常事件
    2. 動(dòng)態(tài)DFM文件應(yīng)用之一:超媒體系統(tǒng)的卡片設(shè)計(jì)
    Delphi多種類(lèi)型的可視部件,如文本部件、編輯部件、圖形圖像部件、數(shù)據(jù)庫(kù)部件、媒體媒放部件和OLE部件等,每一種部件在屏幕中占據(jù)一定的區(qū)域,具有相當(dāng)豐富的表現(xiàn)能力,可以作為卡片中的一種媒體,因此可以利用這些可視部件進(jìn)行超媒體系統(tǒng)的卡片設(shè)計(jì)。
    超媒體卡片設(shè)計(jì)要求卡片中的媒體數(shù)目和媒體種類(lèi)是不受限制的,而且必須能夠修改和存取卡片,因此,采用動(dòng)態(tài)DFM文件是比較合適的。而且如果利用Stream對(duì)象,將卡片存儲(chǔ)在數(shù)據(jù)庫(kù)BLOB字段中,就為把超文本與關(guān)系數(shù)據(jù)庫(kù)技術(shù)結(jié)合起來(lái)創(chuàng)造了契機(jī)。
    下面是超媒體卡片設(shè)計(jì)子系統(tǒng)中的部分源程序,它演示了如何創(chuàng)建對(duì)象、插入對(duì)象和存取動(dòng)態(tài)DFM文件。
    ⑴ 在應(yīng)用程序中注冊(cè)對(duì)象
    procedure TMainForm.FormCreate(Sender: TObject);
    begin
    RegisterClass(TLabel);
    RegisterClass(TEdit);
    RegisterClass(TMemo);
    RegisterClass(TButton);
    RegisterClass(TPanel);
    RegisterClass(TPanelP);
    RegisterClass(TBitBtn);
    …
    end;
    ⑵ 創(chuàng)建和插入對(duì)象
    procedure TMDIChild.FormClick(Sender: TObject);
    var
    Ctrl : TControl;
    Point: TPoint;
    begin
    GetCursorPos(Point);
    Point := BackGround.ScreenToClient(Point);
    case CurToolIndex of
    1 : begin
    Ctrl := TLabel.Create(self);
    TLabel(Ctrl).AutoSize := False;
    TLabel(ctrl).Caption := 'Label'+S;
    TLabel(ctrl).Name := 'Label 1';
    TLabel(ctrl).Top := Point.Y;
    TLabel(ctrl).Left := Point.X;
    TLabel(Ctrl).Height := Round(100*Res/1000/Ratio);
    TLabel(Ctrl).Width := Round(600*Res/1000/Ratio);
    TLabel(Ctrl).Color := clWhite;
    TLabel(Ctrl).Font.Color := clBlack;
    TLabel(Ctrl).Font.Name := 'Roman';
    TLabel(Ctrl).Font.Height := -TLabel(Ctrl).Height;
    TLabel(Ctrl).Font.Pitch := fpFixed;
    TLabel(Ctrl).Enabled := False;
    TLabel(Ctrl).OnClick := LabelClick;
    TLabel(Ctrl).OnMouseMove := ReportPos;
    BackGround.InsertControl(Ctrl);
    CurTool.Down := False;
    CurTool := nil;
    …
    end;
    2: begin
    Ctrl := TEdit.Create(self);
    TEdit(ctrl).AutoSize := True;
    TEdit(ctrl).Top := Point.Y;
    TEdit(ctrl).Left := Point.X;
    TEdit(Ctrl).Height := 20;
    BackGround.InsertControl(Ctrl);
    …
    end;
    3:
    …
    end;
    end;
    ⑵ 存取動(dòng)態(tài)DFM文件
    procedure TMainForm.FileOpen(Sender: TObject);
    begin
    if OpenDialog.Execute then
    begin
    DesignWin := TMDIChild.Create(Application);
    ReadComponentResFile(OpenDialog.FileName, DesignWin);
    DesignWin.Init;
    FileName := OpenDialog.FileName;
    DesignWin.Caption := FFileName;
    end;
    end;
    DesignWin是在TMainForm中定義的TMDIChild類(lèi)型的窗體部件,是卡片設(shè)計(jì)平臺(tái);FFileName是私有變量,用來(lái)保存當(dāng)前編輯的卡片文件名。DesignWin的Init方法實(shí)現(xiàn)如下:
    procedure TMDIChild.Init;
    var
    I: Integer;
    Ctrl: TControl;
    begin
    BackGround.BringToFront;
    with BackGround do
    for I:= 0 to ControlCount - 1 do
    if Controls[I].Name <> ''then
    ObjectIns.ObjectList.Items.AddObject(Controls[I].Name, Controls[I]);
    end;
    BackGround是TPanel類(lèi)型的部件,所有的動(dòng)態(tài)創(chuàng)建對(duì)象都插入到BackGround中,所以,后面調(diào)用BackGround.InsertControl(Ctrl);ObjectIns是個(gè)仿Delphi 的媒體屬性編輯器。
    動(dòng)態(tài)DFM文件的存儲(chǔ)過(guò)程是這樣的:
    procedure TMainForm.FileSave(Sender: TObject);
    begin
    if DesignWin.CurControl <> nil then
    DesignWin.CurControl.Enabled := True;
    WriteComponentResFile(FFilename, DesignWin);
    DesignWin.Caption := FileName;
    end;
    end;
    因?yàn)樵贒esignWin的Init方法中調(diào)用了InsertControl方法,所以在關(guān)閉DesignWin窗口時(shí)要相應(yīng)地調(diào)用RemoveControl,否則在關(guān)閉DesignWin窗口時(shí)會(huì)產(chǎn)生內(nèi)存錯(cuò)誤。
    procedure TMDIChild.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    var
    I: Integer;
    Ctrl: TControl;
    Removed: Boolean;
    begin
    if Modified = True then
    if MessageDlg('Close the form?', mtConfirmation,
    [mbOk, mbCancel], 0) = mrCancel then
    CanClose := False;
    if CanClose = True then
    begin
    repeat
    removed := False;
    I := 0;
    repeat
    if BackGround.Controls[I].Name <> '' then
    begin
    BackGround.RemoveControl(BackGround.Controls[I]);
    Removed := True;
    end;
    I := I + 1
    until (I >= BackGround.ControlCount) or (Removed = True);
    until (Removed = False);
    SendMessage(ObjectIns.Handle, WM_MDICHILDCLOSED, 0, 0);
    end;
    end;