第二十章 开发Delphi对象式数据管理功能(五)
写DFM文件的过程WriteComponentResFie
该过程带有两个参数FileName和InstanceFileName参数指定要写入的DFM文件名Instance参数是TComponent类型的它指定要写入的部件名一般是TForm对象的子类该过程将Instance部件和其拥有的所有部件写入DFM文件
这个过程的意义在于可以在程序运行过程中产生Delphi的窗体部件和在窗体中插入部件并由该函数将窗体写入DFM文件支持了动态DFM文件的重用性
该过程的程序是这样的
procedure WriteComponentResFile(const FileName: string; Instance: TComponent)
var
Stream: TStream;
begin
Stream := TFileStreamCreate(FileName fmCreate)
try
StreamWriteComponentRes(InstanceClassName Instance)
finally
StreamFree;
end;
end;
函数中用FileStream创建文件用Stream对象的WriteComponetRes方法将Instance写入流中
读DFM文件的函数ReadComponentResFile
ReadComponentResFile函数带有两个参数FileName和InstanceFileName参数指定要读DFM文件名Instance参数指定从DFM文件中要读的部件该函数从DFM文件中将Instance和它拥有的所有部件并返回该部件
这个函数的意义在于配合WriteComponentResFile过程的使用支持DFM文件的重用性
该函数的程序是这样的
function ReadComponentResFile(const FileName: string; Instance: TComponent)
TComponent;
var
Stream: TStream;
begin
Stream := TFileStreamCreate(FileName fmOpenRead)
try
Result := StreamReadComponentRes(Instance)
finally
StreamFree;
end;
end;
程序中使用FileStream对象打开由FileName指定的DFM文件然后用Stream对象的ReadComponentRes方法读出Instance并将读的结果作为函数的返回值
读取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 <> ;
if not Result then Exit;
FreeResource(HRsrc)
with TResourceStreamCreate(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得到读出的部件
DFM文件与标准文本文件(TXT文件)的相互转换
在Delphi可视化设计环境中允许程序员在代码编辑器中以文本的方式浏览和修改DFM文件内容当用File/Open命令直接打开DFM文件或者选择窗体设计窗口的弹出式菜单上的View as Text命令时就会在编辑器中出现文本形式的信息我们姑且将这种文本形式称之为窗体设计脚本Delphi提供的这种脚本编辑功能是对Delphi可视化设计的一大补充当然这个脚本编辑能力是有限制的比方说不能在脚本任意地添加和删除部件因为代码和DFM脚本是紧密相连的任意添加和修改会导致不一致性然而在动态生成的DFM文件中就不存在这一限制后面会介绍DFM动态生成技术的应用
实际上DFM文件内容是二进制数据它的脚本是经过Delphi开发环境自动转化的而且Delphi VCL中的Classes库单元中提供了在二进制流中的文件DFM和它的脚本之相互转化的过程它们是ObjectBinaryToText和ObjectTextBinaryObjectResourceToText和ObjectTextToResource
ObjectBinaryToText过程将二进制流中存储的部件转化为基于文本的表现形式这样就可以用文本处理函数进行处理还可以用文本编辑器进行查找和替代操作最后可以将文本再转化成二进制流中的部件
ObjectBinaryToText过程的主程序是这样的
procedure ObjectBinaryToText(Input Output: TStream)
var
NestingLevel: Integer;
SaveSeparator: Char;
Reader: TReader;
Writer: TWriter;
procedure WriteIndent;
const
Blanks: array[] of Char = ;
var
I: Integer;
begin
for I := to NestingLevel do WriterWrite(Blanks SizeOf(Blanks))
end;
procedure WriteStr(const S: string)
begin
WriterWrite(S[] Length(S))
end;
procedure NewLine;
begin
WriteStr(##)
WriteIndent;
end;
procedure ConvertHeader;
begin
…
end;
procedure ConvertBinary;
begin
…
end;
procedure ConvertValue;
begin
…
end;
procedure ConvertProperty;
begin
…
end;
procedure ConvertObject;
begin
…
end;
begin
NestingLevel := ;
Reader := TReaderCreate(Input )
SaveSeparator := DecimalSeparator;
DecimalSeparator := ;
try
Writer := TWriterCreate(Output )
try
ReaderReadSignature;
ConvertObject;
finally
WriterFree;
end;
finally
DecimalSeparator := SaveSeparator;
ReaderFree;
end;
end;
过程中调用的ConvertObject过程是个递归过程用于将DFM文件中的每一个部件转化为文本形式因为由于部件的拥有关系所以部件成嵌套结构采用递归是最好的方式
procedure ConvertObject;
begin
ConvertHeader;
Inc(NestingLevel)
while not ReaderEndOfList do ConvertProperty;
ReaderReadListEnd;
while not ReaderEndOfList do ConvertObject;
ReaderReadListEnd;
Dec(NestingLevel)
WriteIndent;
WriteStr(end##)
end;
NestStingLevel变量表示部件的嵌套层次WriteIndent是写入每一行起始字符前的空格ConvertHeader过程是处理部件的继承标志信息转换成的头信息文本有两种形式
Inherited TestForm: TTestForm[]
或者
Object TestForm: TTestForm
前者是ffInherited和ffChildPos置位后面是都没置位
ConvertProperty过程用于转化属性
procedure ConvertProperty;
begin
WriteIndent;
WriteStr(ReaderReadStr)
WriteStr( = )
ConvertValue;
WriteStr(##)
end;
WriteIndent语句写入属性名前的空格WriteStr(ReaderReadStr)语句写入属性名ConvertValue过程根据属性的类型将属性值转化为字符串然后写入流中
[] [] [] []