| 网站首页 | 文章中心 | 电子书下载 | 矢量图库 | 视频教程 | 素材下载 | 程序代码下载 | JS代码 | 论坛 | 
常用软件类:
|杀毒安全 |联络聊天 |网络软件 |多媒体类 |系统工具 |图形图像 |系统工具 |应用软件 |行业软件
开发设计类:
|动画制作 |图像处理 |3D设计 |操作系统 |站长学院 |网络相关 |WEB设计 |数据库类 |程序开发
第二十章 开发Delphi对象式数据管理功能(五)
作者:佚名    文章来源:网络    点击数:    更新时间:2006-11-3
 

20.3.1.1写DFM文件的过程:WriteComponentResFie
   该过程带有两个参数FileName和Instance。FileName参数指定要写入的DFM文件名,Instance参数是TComponent类型的,它指定要写入的部件名,一般是TForm对象的子类。该过程将Instance部件和其拥有的所有部件写入DFM文件。
  这个过程的意义在于,可以在程序运行过程中产生Delphi的窗体部件和在窗体中插入部件,并由该函数将窗体写入DFM文件,支持了动态DFM文件的重用性。
  该过程的程序是这样的:
 
procedure WriteComponentResFile(const FileName: string; Instance: TComponent);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
Stream.WriteComponentRes(Instance.ClassName, Instance);
finally
Stream.Free;
end;
end;
 
  函数中,用FileStream创建文件,用Stream对象的WriteComponetRes方法将Instance写入流中。
 
20.3.1.2 读DFM文件的函数:ReadComponentResFile
 
ReadComponentResFile函数带有两个参数FileName和Instance。FileName参数指定要读DFM文件名,Instance参数指定从DFM文件中要读的部件。该函数从DFM文件中将Instance和它拥有的所有部件,并返回该部件。
  这个函数的意义在于,配合WriteComponentResFile过程的使用支持DFM文件的重用性。
  该函数的程序是这样的:
 
function ReadComponentResFile(const FileName: string; Instance: TComponent): 
TComponent;
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
try
Result := Stream.ReadComponentRes(Instance);
finally
Stream.Free;
end;
end;
 
  程序中使用FileStream对象打开由FileName指定的DFM文件,然后用Stream对象的ReadComponentRes方法读出Instance,并将读的结果作为函数的返回值。
 
20.3.1.3 读取Delphi应用程序资源中的部件
 
  函数InternalReadComponentRes可以读取Delphi应用程序资源中的部件。Delphi 的DFM文件在程序经过编译链接后被嵌入应用程序的资源中,而且格式发生了改变,即少了资源文件头。
在第一节中曾经介绍过TResourceStream对象,该对象是操作资源媒介上的数据的。函数InternalReadComponentRes用了TResourceStream。程序是这样的:
 
function InternalReadComponentRes(const ResName: string;
var Instance: TComponent): Boolean;
var
HRsrc: THandle;
begin { 避免“EResNotFound”异常事件的出现 }
HRsrc := FindResource(HInstance, PChar(ResName), RT_RCDATA);
Result := HRsrc <> 0;
if not Result then Exit;
FreeResource(HRsrc);
with TResourceStream.Create(HInstance, ResName, RT_RCDATA) do
try
Instance := ReadComponent(Instance);
finally
Free;
end;
Result := True;
end;
 
  HInstance是一个Delphi VCL定义的全局变量,代表当前应用程序的句柄。函数用了资源访问API函数FindResource来测定是否存在ResName所描述资源。因为在TResourceStream的创建过程还有FindResource等操作,所以函数中调用了FreeResource。最后函数调用了Stream对象的ReadComponent方法读出部件。因为函数的Instance是var类型的参数,所以可以访问Instance,得到读出的部件。
 
20.3.1.4 DFM文件与标准文本文件(TXT文件)的相互转换
 
  在Delphi可视化设计环境中,允许程序员在代码编辑器中以文本的方式浏览和修改DFM文件内容。当用File/Open命令直接打开DFM文件或者选择窗体设计窗口的弹出式菜单上的View as Text命令时,就会在编辑器中出现文本形式的信息。我们姑且将这种文本形式称之为窗体设计脚本。Delphi提供的这种脚本编辑功能是对Delphi可视化设计的一大补充。当然这个脚本编辑能力是有限制的,比方说不能在脚本任意地添加和删除部件,因为代码和DFM脚本是紧密相连的,任意添加和修改会导致不一致性。然而在动态生成的DFM文件中,就不存在这一限制,后面会介绍DFM动态生成技术的应用。
  实际上,DFM文件内容是二进制数据,它的脚本是经过Delphi开发环境自动转化的,而且Delphi VCL中的Classes库单元中提供了在二进制流中的文件DFM和它的脚本之相互转化的过程。它们是ObjectBinaryToText和ObjectTextBinary、ObjectResourceToText和ObjectTextToResource。
ObjectBinaryToText过程将二进制流中存储的部件转化为基于文本的表现形式,这样就可以用文本处理函数进行处理,还可以用文本编辑器进行查找和替代操作,最后可以将文本再转化成二进制流中的部件。
  ObjectBinaryToText过程的主程序是这样的:
 
procedure ObjectBinaryToText(Input, Output: TStream);
var
NestingLevel: Integer;
SaveSeparator: Char;
Reader: TReader;
Writer: TWriter;
 
procedure WriteIndent;
const
Blanks: array[0..1] of Char = ’ ’;
var
I: Integer;
begin
for I := 1 to NestingLevel do Writer.Write(Blanks, SizeOf(Blanks));
end;
 
procedure WriteStr(const S: string);
begin
Writer.Write(S[1], Length(S));
end;
 
procedure NewLine;
begin
WriteStr(#13#10);
WriteIndent;
end;
 
procedure ConvertHeader;
begin

end;
 
procedure ConvertBinary;
begin

end;
 
procedure ConvertValue;
begin

end;
 
procedure ConvertProperty;
begin

end;
 
procedure ConvertObject;
begin

end;
 
begin
NestingLevel := 0;
Reader := TReader.Create(Input, 4096);
SaveSeparator := DecimalSeparator;
DecimalSeparator := ’.’;
try
Writer := TWriter.Create(Output, 4096);
try
Reader.ReadSignature;
ConvertObject;
finally
Writer.Free;
end;
finally
DecimalSeparator := SaveSeparator; 
Reader.Free;
end;
end;
 
  过程中调用的ConvertObject过程是个递归过程,用于将DFM文件中的每一个部件转化为文本形式。因为由于部件的拥有关系,所以部件成嵌套结构,采用递归是最好的方式:
 
procedure ConvertObject;
begin
ConvertHeader;
Inc(NestingLevel);
while not Reader.EndOfList do ConvertProperty;
Reader.ReadListEnd;
while not Reader.EndOfList do ConvertObject;
Reader.ReadListEnd;
Dec(NestingLevel);
WriteIndent;
WriteStr(’end’#13#10);
end;
 
  NestStingLevel变量表示部件的嵌套层次。WriteIndent是写入每一行起始字符前的空格,ConvertHeader过程是处理部件的继承标志信息。转换成的头信息文本有两种形式。
  Inherited TestForm1: TTestForm[2]
  或者:
Object TestForm1: TTestForm
 
前者是ffInherited和ffChildPos置位,后面是都没置位。
  ConvertProperty过程用于转化属性。
 
procedure ConvertProperty;
begin
WriteIndent;
WriteStr(Reader.ReadStr);
WriteStr(’ = ’);
ConvertValue;
WriteStr(#13#10);
end;
 
  WriteIndent语句写入属性名前的空格,WriteStr(Reader.ReadStr)语句写入属性名ConvertValue过程根据属性的类型将属性值转化为字符串,然后写入流中。
  ObjectTextToBinary过程执行的功能与ObjectBinaryToText相反,将TXT文件转换为二进制流中的部件,而且只要TXT文件内容的书写符合DFM脚本语法,ObjectTextToBinary可将任何程序生成的TXT文件转换为部件,这一功能也为DFM 文件的动态生成和编辑奠定了基础。ObjectTextToBinary过程的主程序如下:
 
procedure ObjectTextToBinary(Input, Output: TStream);
var
SaveSeparator: Char;
Parser: TParser;
Writer: TWriter;
 
  …
  
begin
Parser := TParser.Create(Input);
SaveSeparator := DecimalSeparator;
DecimalSeparator := ’.’;
try
Writer := TWriter.Create(Output, 4096);
try
Writer.WriteSignature;
ConvertObject;
finally
Writer.Free;
end;
finally
DecimalSeparator := SaveSeparator;
Parser.Free;
end;
end;
 
  在程序流程和结构上与ObjectBinaryToText差不多。ConvertObject也是个递归过程:
 
procedure ConvertObject;
var
InheritedObject: Boolean;
begin
InheritedObject := False;
if Parser.TokenSymbolIs(’INHERITED’) then
InheritedObject := True 
else
Parser.CheckTokenSymbol(’OBJECT’);
Parser.NextToken;
ConvertHeader(InheritedObject);
while not Parser.TokenSymbolIs(’END’) and
not Parser.TokenSymbolIs(’OBJECT’) and
not Parser.TokenSymbolIs(’INHERITED’) do ConvertProperty;
Writer.WriteListEnd;
while not Parser.TokenSymbolIs(’END’) do ConvertObject;
Writer.WriteListEnd;
Parser.NextToken;
end;
 
  DFM文件与DFM脚本语言之间相互转换的任务由ObjectResourceToText和ObjextTextToResource两个过程完成。
 
procedure ObjectResourceToText(Input, Output: TStream);
begin
Input.ReadResHeader;
ObjectBinaryToText(Input, Output);
end;
 
ObjectTextToResource过程就比较复杂,因为DFM文件资源头中要包含继承标志信息,因此在调用ObjectTextToBinary后,就读取标志信息,然后写入资源头。
 
procedure ObjectTextToResource(Input, Output: TStream);
var
Len: Byte;
Tmp: Longint;
MemoryStream: TMemoryStream;
MemorySize: Longint;
Header: array[0..79] of Char;
begin
MemoryStream := TMemoryStream.Create;
try
ObjectTextToBinary(Input, MemoryStream);
MemorySize := MemoryStream.Size;
FillChar(Header, SizeOf(Header), 0);
MemoryStream.Position := SizeOf(Longint); { Skip header }
MemoryStream.Read(Len, 1);
if Len and $F0 = $F0 then
begin
if ffChildPos in TFilerFlags((Len and $F0)) then
begin
MemoryStream.Read(Len, 1);
case TValueType(Len) of
vaInt8: Len := 1;
vaInt16: Len := 2;
vaInt32: Len := 4;
end;
MemoryStream.Read(Tmp, Len);
end;
MemoryStream.Read(Len, 1);
end;
MemoryStream.Read(Header[3], Len);
StrUpper(@Header[3]);
Byte((@Header[0])^) := $FF;
Word((@Header[1])^) := 10;
Word((@Header[Len + 4])^) := $1030;
Longint((@Header[Len + 6])^) := MemorySize;
Output.Write(Header, Len + 10);
Output.Write(MemoryStream.Memory^, MemorySize);
finally
MemoryStream.Free;
end;
end;
 
20.3.1.5 动态DFM文件应用揭秘
 
  1. 动态DFM文件概述
动态DFM文件是相对于静态DFM文件而言。所谓静态DFM文件是指在Delphi开发环境中设计的窗体文件。窗体的设计过程就是程序的编制过程。因此,动态DFM文件就是指在程序运行过程生成或存取的DFM文件。
  动态DFM文件的创建和使用分别如下两种情况:
  ● 在程序运行过程中,由Create方法动态生成窗体或部件,然后动态生成其它部件插入其中生成DFM文件
  ● 在Delphi开发环境中,设计生成DFM文件,然后用DFM 文件存取函数,或者用Stream对象和Filer对象的方法,将DFM文件读入内存,进行处理,最后又存入磁盘中
 
  由Delphi的窗体设计的常规方法生成的DFM文件在程序运行一开始就规定了部件的结构。因为在窗体设计过程中,窗体中的每个部件都在程序的对象声明中定义了部件变量。这种固定的结构虽然能方便应用,但以牺牲灵活性为代价。
  在Delphi应用程序中有时需要在运行过程中创建控制,然后将该控制插入另一个部件中。例如:
 
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;
 
  动态插入控制的优点是可以在任何时刻、任意位置插入任意数量的任何类型的控制。因为应用程序需求在很多情况下是在程序运行中才知道的,所以动态插入控制就显得很重要。而且在很多情况下,需要保存这些界面元素,留待程序再次调用。例如应用程序界面的定制、系统状态的保存、对话框的保存等。这时生成动态DFM文件是最佳选择。
  动态插入控制的不足之处是在插入控制前,无法直观地看到控制的大小、风格、位置等,也就是动态插入控制的过程是非可视化的。但可以借助于静态DFM文件的可视化设计。这就是生成和使用动态DFM文件的第二种方法。也就是在应用程序运行前,在Delphi开发环境中,使用可视化开发工具设计所需窗口或部件的样式,以DFM文件保存。然后在应用程序运行过程中,将DFM文件读入内存。Delphi的Stream对象和Filer对象在读取DFM文件时,会根据DFM文件的内容自动创建部件及其拥有的所有部件。
  在使用动态DFM文件时有两点需要注意。
 ● 每一个动态插入的控制或部件必须在程序中调用RegisterClass进行注册
  ● 读入DFM文件自动创建部件后,如果调用了InsertControl方法, 则在关闭窗口时要调用RemoveControl方法移去该控制,否则会产生异常事件
 
  2. 动态DFM文件应用之一:超媒体系统的卡片设计
  Delphi多种类型的可视部件,如文本部件、编辑部件、图形图像部件、数据库部件、媒体媒放部件和OLE部件等,每一种部件在屏幕中占据一定的区域,具有相当丰富的表现能力,可以作为卡片中的一种媒体,因此可以利用这些可视部件进行超媒体系统的卡片设计。
  超媒体卡片设计要求卡片中的媒体数目和媒体种类是不受限制的,而且必须能够修改和存取卡片,因此,采用动态DFM文件是比较合适的。而且如果利用Stream对象,将卡片存储在数据库BLOB字段中,就为把超文本与关系数据库技术结合起来创造了契机。
  下面是超媒体卡片设计子系统中的部分源程序,它演示了如何创建对象、插入对象和存取动态DFM文件。
  ⑴ 在应用程序中注册对象
 
procedure TMainForm.FormCreate(Sender: TObject);
begin
RegisterClass(TLabel);
RegisterClass(TEdit);
RegisterClass(TMemo);
RegisterClass(TButton);
RegisterClass(TPanel);
RegisterClass(TPanelP);
RegisterClass(TBitBtn);

end;
 
⑵ 创建和插入对象
 
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; 

[1] [2] 下一页


  • 上一篇文章:

  • 下一篇文章: 没有了
  • 相关文章