在Delphi可视化设计环境中,允许程序员在代码编辑器中以文本的方式浏览和修改DFM文件内容。当用File/Open命令直接打开DFM文件或者选择窗体设计窗口的弹出式菜单上的View as Text命令时,就会在编辑器中出现文本形式的信息。在一些资料中将这种文本形式称之为窗体设计脚本。Delphi提供的这种脚本编辑功能是对Delphi可视化设计的一大补充。当然这个脚本编辑能力是有限制的,比方说不能在脚本任意地添加和删除部件,因为代码和DFM脚本是紧密相连的,任意添加和修改会导致不一致性。但在动态生成的DFM文件中,就不存在这一限制。 实际上,DFM文件内容是二进制数据,它的脚本是经过Delphi开发环境自动转化的,而且Delphi VCL中的Classes库单元提供了在二进制流中的文件DFM和它的脚本之相互转化的过程。它们是ObjectBinaryToText和ObjectTextToBinary、ObjectResourceToText和ObjectTextToResource。 ObjectBinaryToText过程将二进制流中存储的部件转化为基于文本的表现形式,这样就可以用文本处理函数进行处理,还可以用文本编辑器进行查找和替代操作,最后可以将文本再转化成二进制流中的部件。 ObjectTextToBinary过程执行的功能与ObjectBinaryToText相反,将TXT文件转换为二进制流中的部件,而且只要TXT文件内容的书写符合DFM脚本语法,ObjectTextToBinary可将任何程序生成的TXT文件转换为部件,这一功能也为DFM文件的动态生成和编辑奠定了基础。
如何在运行过程中将本窗体保存成一个文本格式的.dfm文件? zswang(伴水) (2001-11-21 9:52:59) 得0分 function ComponentToString(Component: TComponent): string; var BinStream: TMemoryStream; StrStream: TStringStream; s: string; begin BinStream := TMemoryStream.Create; try StrStream := TStringStream.Create(s); try BinStream.WrITeComponent(Component); BinStream.Seek(0, soFromBeginning); ObjectBinaryToText(BinStream, StrStream); StrStream.Seek(0, soFromBeginning); Result := StrStream.DataString; finally StrStream.Free; end; finally BinStream.Free end; end; { ComponentToString } function StringToComponent(Value: string; Instance: TComponent): TComponent; var StrStream: TStringStream; BinStream: TMemoryStream; begin StrStream := TStringStream.Create(Value); try BinStream := TMemoryStream.Create; try ObjectTextToBinary(StrStream, BinStream); BinStream.Seek(0, soFromBeginning); Result := BinStream.ReadComponent(Instance); finally BinStream.Free; end; finally StrStream.Free; end; end; { StringToComponent } 回复人: zswang(伴水) (2001-11-21 9:54:28) 得0分 procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Text := ComponentToString(Self); end; 回复人: zswang(伴水) (2001-11-21 9:58:13) 得0分 procedure TForm1.Button2Click(Sender: TObject); begin StringToComponent( 'object Label1: TLabel'#13#10 + ' Left = 232'#13#10 + ' Top = 56'#13#10 + ' Width = 26'#13#10 + ' Height = 13'#13#10 + ' Caption = #20320#22909'#13#10 + ' Font.Charset = GB2312_CHARSET'#13#10 + ' Font.Color = clRed'#13#10 + ' Font.Height = -13'#13#10 + ' Font.Name = #23435#20307'#13#10 + ' Font.Style = []'#13#10 + ' ParentFont = False'#13#10 + 'end'#13#10, Label1); end; //要注册类 ==end================================= 好了,理解了上面的这段文字,一些朋友就会自然想到,利用这几个函数应该可以弄出点有用的东西出来,我就弄出了一点应用,并全面应用到了项目中,现在我来给大家完整描述出来:
首先我要求我的程序有如下能力: 1. 我的程序的窗体是可以动态替换的,不用编译Exe,只要替换一个DFM窗体设计脚本就可以了(当然,你可以重新包装一下这个DFM文件,比如换成txt后缀名等)。 2. 我可以预览所有的DFM文件,让它变成实际的Form察看。 不要小看这两点,在很多情况下,这意义非常重大,举几个例子①开发阶段,可以把界面设计和程序设计完全分开,分工进行②现场维护时,有些界面的调整和功能设置不需要再找源代码到Delphi下去编译一遍了,老出差做Mis类的朋友应该能从这点体会出好处③某些功能界面的升级简单了不少,只要让用户下载一个DFM文件覆盖原来的就可以了。 好,不费话了,下面详细说明怎么达到以上两点要求。 显然我们要让一段文本变成一个Form,那么就用这个函数: function StringToComponent(Value: string; Instance:TComponent): TComponent; var StrStream:TStringStream; BinStream: TMemoryStream; begin StrStream := TStringStream.Create(Value); try BinStream := TMemoryStream.Create; try ObjectTextToBinary(StrStream, BinStream); BinStream.Seek(0, soFromBeginning); Result := BinStream.ReadComponent(Instance); finally BinStream.Free; end; finally StrStream.Free; end; end; 但是,所有的Class必须是注册过的,例如,如下的Form1FRM.DFM文件 object Form1: TForm1 Left = 222 Top = 168 Width = 485 Height = 290 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Panel1: TPanel Left = 0 Top = 0 Width = 477 Height = 33 Align = alTop TabOrder = 0 object BitBtn1: TBITBtn Left = 4 Top = 4 Width = 75 Height = 25 Caption = 'OK' TabOrder = 0 end end object Memo1: TMemo Left = 0 Top = 33 Width = 477 Height = 230 Align = alClient TabOrder = 1 end end 你应该这么使用, var list:TstringList;form:TForm … list.Lines.LoadFromFile(‘Form1FRM.DFM’); RegisterClass(TForm1); RegisterClass(TPanel); RegisterClass(TBITBtn); RegisterClass(TMemo); form := StringToComponent(list.Lines.Text,nil); form.ShowModal(); … 这样就能显示出一个窗体了。 但是这有个问题,Delphi自带的VCL控件是固定的,用RegisterClass(…)注册一遍没有问题,可TForm1不是,如果连TForm1都要注册的话,就无法达成第2点要求。我们可以变通一下,因为所有的Form都是从Tform继承的,所以,应该都可以用注册Tform来取代,因此,有了下面这样一个函数: function LoadTextForm(FileName:String):TForm; [1] [2] [3] 下一页 |