汇编 | Java | C# | Delphi | C/C++ |

DELPHI基础教程 第二十章 开发Delphi对象式数据管理功能(四)

第二十章 开发Delphi对象式数据管理功能(四)

本文来自织梦

20.2.3 TReader对象 

织梦内容管理系统

  TReader对象是可实例化的用于从相联系的流中读取数据的Filer对象。TReader对象从TFiler继承下来,除了从TFiler继承的属性和方法外,TReader声明了不少属性、方法和事件。 织梦内容管理系统

  OwnerParent属性用于表示从Reader对象的流中读取的部件的拥有者和双亲结点。OnErrorOnFindMethodOnSetName事件使应用程序在运行中读数据时能定制响应方式。除了覆盖了一些从TFiler对象中继承的方法外,TReader对象还定义大量的读不同类型的数据和触发事件的方法。

织梦好,好织梦

  内容来自dedecms

20.2.3.1 TReader对象的属性和方法

本文来自织梦

 

dedecms.com

  1. Owner属性 本文来自织梦

  声明:property Owner: TComponent; copyright dedecms

Reader对象的Owner属性存储了将用来给从Reader的流中读出的部件的Owner属性赋值的部件。

dedecms.com

  2. Parent属性

织梦好,好织梦

  声明:property Parent: TComponent;

本文来自织梦

Parent属性存储将用来给从Reader的流中读出所有控制的Parent属性赋值的部件。 copyright dedecms

  3. Position属性 织梦好,好织梦

  声明:propertion: Longint;

内容来自dedecms

Reader对象的Position属性表示相联的流中读的当前位置。Position的值还应包括读缓冲区的大小。对于Reader 对象,Position的值大于流的Position 的值。如果将Position的值设得超过当前缓冲区,将引起调用FlushBuffer

dedecms.com

  4. BeginReferences方法 dedecms.com

  声明:procedure BeginReferences; 织梦好,好织梦

BeginReferences方法启动一连串关于读部件的命令,这些部件包含相互间的交叉引用。在使用上通常和FixupReferencesEndReferences一起放在Tryfinally程序块中。

本文来自织梦

  在调用了BeginReferences后,Reader对象创建读取所有对象和名字的列表。所有的独立对象被读出后,调用FixupReferences方法将名字的相互从流中转移到对象实例中。最后调用EndReferences方法释放列表。

内容来自dedecms

  处理部件相互引用的程序块形式如下: 织梦好,好织梦

 

copyright dedecms

BeginReferences; { 创建临时列表 }

内容来自dedecms

try

本文来自织梦

{ 读出所有部件并将它们的名字放在一临时列表中 }

copyright dedecms

织梦好,好织梦

FixupReferences; { } 内容来自dedecms

finally 内容来自dedecms

EndReferences; { 释放临时列表 }

copyright dedecms

end; copyright dedecms

 

织梦好,好织梦

  5. FixUpReferences方法 织梦内容管理系统

  声明:procedure FixupReferences; 本文来自织梦

FixupReferences方法分解从流中读出的存在各种相互依赖部件的引用关系。FixupReferences总在tryfinally块中并配合BeginReferencesEndReferences一起使用。

织梦内容管理系统

  6. EndReferences方法

内容来自dedecms

  声明:procedure EndReferences; copyright dedecms

EndReferences方法终止处理相互引用的块操作,释放对象列表。它总配合BeginReferencesFixupReferences一起使用。 copyright dedecms

  7. ReadListBegin方法

copyright dedecms

  声明:procedure ReadListBegin; copyright dedecms

ReadListBegin方法从Reader对象相联的流中读取列表开始标志。如果流中紧接着要读取的项目不是一个由WritelistBegin方法写入的列表起始标志,ReadListBegin将引起一个读异常事件。 内容来自dedecms

  通常在调用ReadlistBegin方法之后,紧跟着一个读项目的循环,循环以EndfList方法返回True 终止条件。这时,预示流中的下一个项目是列表结束标志,需要调用ReadListEnd方法。

内容来自dedecms

  8. ReadListEnd方法

内容来自dedecms

  声明:procedure ReadListEnd; 织梦好,好织梦

ReadListEnd 方法从流中读取列表结束标志。如果所读的项目不是一个列表结束标志,ReadListEnd方法引发一个EReadError异常事件。

内容来自dedecms

  9. EndOfList方法

本文来自织梦

  声明:function EndOfList: Boolean; 本文来自织梦

如果Reader对象读到项目列表结果标志,EndOfList方法返回True 织梦好,好织梦

  TStrings对象在从Reader对象读取项目列表时使用了ReadListBeginReadListEnd方法。下面的ReadDataTStrings的方法,用于在DefineProperties方面中读string数据。

织梦好,好织梦

 

织梦内容管理系统

procedure TStrings.ReadData(Reader: TReader); 本文来自织梦

begin

织梦内容管理系统

Reader.ReadListBegin; { 读列表开始标志 } dedecms.com

Clear; { 清除已有的字符串 } dedecms.com

while not Reader.EndOfList do { 只要还有数据 } copyright dedecms

Add(Reader.ReadString); { …读一个字符串并将其加在列表中 } 织梦内容管理系统

Reader.ReadListEnd; { 越过列表结束标志 } dedecms.com

end;

本文来自织梦

 

内容来自dedecms

10. ReadSignature方法 内容来自dedecms

  声明:procedure ReadSignature;

dedecms.com

ReadSignature方法从流中读取部件之前首先调用ReadSignature方法。在载入对象之前检测标签。Reader对象就能防止疏忽大意,导致读取无效或过时的数据。Filer标签是四个字符,对于Delphi 2.0,该标签是“TPF0”。 本文来自织梦

  11. ReadPrefix方法

copyright dedecms

  声明:procedure ReadPrefix(var Plags: TFilerFlags; var AChild, Pos: Integer);

本文来自织梦

ReadPrefix方法的功能与ReadSignature的很相象,只不过它是读取流中部件前面的标志(PreFix)。当一个Write对象将部件写入流中时,它在部件前面预写了两个值,第一个值是指明部件是否是从祖先窗体中继承的窗体和它在窗体中的位置是否重要的标志;第二个值指明它在祖先窗体创建次序。ReadComponent方法自动调用ReadPrefix。但如果需要独立读取部件的预读标志,也可直接调用该方向。 织梦内容管理系统

  12. OnFindMethod事件 织梦好,好织梦

  声明:property OnFindMethod: TFindMethodEvent; 本文来自织梦

OnFindMethod事件,发生在Reader对象读取对象的方法指针时,属性为方法指针的通常都是事件。

内容来自dedecms

  响应OnFindMethod事件的理由,通常是处理过程找不到方法的情况。在FindMethod方法没有找到由Name指定的方法的情况下,如果它将OnFindMethod方法的Error 参数设为True,将引起ReadError异常事件;反之,将Error参数置为False,将防止FindMethod方法引发异常事件。 本文来自织梦

  13. Error方法 织梦内容管理系统

  声明:function Error(const Message: String): Boolean; virtual; 内容来自dedecms

Error方法定义在Reader对象的protected部分,它是用于Reader对象的OnError事件。其返回值决定是否继续错误处理过程。如果返回值为True,则表示用程序应当继续错误处理;如果返回值为False,则表示错误情况被忽略。

dedecms.com

  如果读部件或属性出错。Reader对象调用Error方法。缺省情况下,Error将返回值设为False,然后调用OnError事件处理过程。 织梦好,好织梦

  TReader对象总是在tryexcept程序块的except部分,并提供用户忽略错误的机会。Error的使用方法如下: dedecms.com

  dedecms.com

  try

内容来自dedecms

{ 读部件 } copyright dedecms

except

本文来自织梦

on E: Exception do

dedecms.com

begin 本文来自织梦

…{ 执行一些清除操作 }

本文来自织梦

if Error(E.Message) then raise; dedecms.com

end;

内容来自dedecms

end;

织梦内容管理系统

  本文来自织梦

  14. OnError事件 织梦内容管理系统

  声明:property OnError: TReaderError;

dedecms.com

Reader对象读取数据出错时将引发OnError事件。通过处理OnError事件,可以有选择地处理或忽略错误。

内容来自dedecms

  传给OnError事件处理过程的最后一个参数是名为Handledvar参数。在缺省情况下,Error方法将Handled置为True。这将阻止错误更进一步处理。如果事件处理过程仍旧将Handled置为FalseReader对象将引发一个EReadError异常事件。

本文来自织梦

 

内容来自dedecms

15. SetName方法

织梦好,好织梦

  声明:procedure SetName(Component: TComponent; var Name: String virtual);

dedecms.com

SetName方法允许Reader对象在将从流中读取的部件的Name值赋给部件的Name属性前修改Name值。ReadComponent方法在读取部件的属性值和其它数据前先读部件的类型和名字在读完名字后,ReadComponent将所读的名字作为Name参数传给SetNameName 是个var参数,因此SetName能在返回前修改字符串值。SetName还调用了OnSetName事件处理过程,将名字字符串作为var参数传入事件处理过程中,因此,事件处理过程也可修改字符串的值。 内容来自dedecms

  16. OnSetName事件 本文来自织梦

  声明:property OnSetName: TSetNameEvent; copyright dedecms

OnSetName事件发生在Read对象设置部件的Name属性前,OnSetName事件处理过程的var参数Name参数是一个var参数,因此,事件处理过程再将Name赋给部件前,可以修改Name的值。这对于想过滤窗体中部件的名字是很有帮助的。 织梦内容管理系统

  下面的OnSetName事件处理过程,命名了名字中包含“Button”的部件,并用“PushButton”替代。 本文来自织梦

  织梦好,好织梦

procedure TForm1.ReaderSetName(Reader: TReader; Component: TComponent;

本文来自织梦

var Name: string); dedecms.com

var

织梦好,好织梦

ButtonPos: Integer;

本文来自织梦

begin

本文来自织梦

ButtonPos := Pos('Button', Name);

dedecms.com

if ButtonPos <> 0 then

织梦好,好织梦

Name := Copy(Name, 1, ButtonPos - 1) + 'PushButton' +

dedecms.com

Copy(Name, ButtonPos + 6, Length(Name));

copyright dedecms

end;

copyright dedecms

 

dedecms.com

17. ReadValue方法

织梦好,好织梦

  声明:function ReadValue: TValueType; 内容来自dedecms

ReadValue方法读取流中紧着的项目的类型,函数返回后,流的指针移到值类型指示符之后。

织梦内容管理系统

  TValueType是枚举类型。存储在Filer对象的流中的每个项目之前都有一个字节标识该项目的类型,在读每个项目之前都要读取该字节,以指导调用哪个方法来闱取项目。该字节的值就TValuetype定义的值类型之一。 copyright dedecms

  18. NextValue方法 copyright dedecms

  声明:function Nextvalue: TValuetype;

dedecms.com

Nextvalue方法的作用也是返回Reader对象流中紧接着的项目的类型,它与ReadValue的区别在于并不移动指针位置。 copyright dedecms

  19. ReadBoolean方法 织梦内容管理系统

  声明:function ReadBoolean: Boolean;

本文来自织梦

ReadBoolean方法从Reader对象的流中读取一个布尔值,并相应地移动流位置指针。 copyright dedecms

  20ReadChar方法 织梦内容管理系统

  声明:function ReadChar: char; 本文来自织梦

ReadChar方法从Reader对象的流中读取一个字符。

本文来自织梦

  21. ReadFloat方法 本文来自织梦

  声明:function ReadFloat: Extended; dedecms.com

  ReadFloat方法从流中读取浮点数。 本文来自织梦

  20. ReadIdent方法 dedecms.com

  声明:function ReadIdent: string; 本文来自织梦

ReadIdent方法从流中读取标识符。

dedecms.com

  23. ReadInteger方法

copyright dedecms

  声明:function ReadInteger: Longin

dedecms.com

ReadInteger方法从流中读取整型数字。 织梦好,好织梦

24.ReadString方法

织梦内容管理系统

  声明:function Read String: string;

内容来自dedecms

  ReadString方法从Reader对象的流中读取一个字符串,并返回字符串中的内容。该字符串是由Writer对象的WriteString方法写入。 copyright dedecms

 

copyright dedecms

20.2.3.2 TReader对象的实现 内容来自dedecms

 

织梦内容管理系统

  Filer对象的作用主要是Delphi用来在DFM文件中读写各种类型的数据(包括部件对象)。这些数据的一个本质特征是变长,而且Filer对象将读写数据操作抽象化,包装成对象提供了大量的读写方法,方便了程序的调用。因此在应用程序中可以广泛使Filer对象,充分利用Delphi的面向对象技术。而且Filer对象与Stream对象捆绑在一起,一方面可以在各种存储媒介中存取任意格式的数据;另一方面,由于充分利用面向对象的动态联编,各种读写方法的使用方法是一致的,因此,方法调用很简单。下面我们着重介绍Reader 对象中与读写数据操作有关的属性和方法的实现。 织梦内容管理系统

  1. TReader属性的实现 copyright dedecms

  在TReader对象的属性实现中我们重点介绍Position的实现。

本文来自织梦

  Position属性的定义了使用了读写控制,它们分别是GetPositionSetPosition方法。

织梦好,好织梦

 

copyright dedecms

TReader = class(TFiler)

本文来自织梦

private 织梦内容管理系统

织梦好,好织梦

function GetPosition: Longint;

织梦好,好织梦

procedure SetPosition(Value: Longint); 织梦好,好织梦

public

织梦好,好织梦

copyright dedecms

property Position: Longint read GetPosition write SetPosition; 内容来自dedecms

end;

织梦内容管理系统

  本文来自织梦

Postition的读写控制方法如下: 本文来自织梦

  本文来自织梦

function TReader.GetPosition: Longint; 织梦内容管理系统

begin 织梦好,好织梦

Result := FStream.Position + FBufPos; 织梦内容管理系统

end;

本文来自织梦

 

织梦内容管理系统

procedure TReader.SetPosition(Value: Longint); copyright dedecms

begin

dedecms.com

FStream.Position := Value;

织梦好,好织梦

FBufPos := 0;

内容来自dedecms

FBufEnd := 0;

织梦好,好织梦

end;

织梦好,好织梦

  copyright dedecms

TReader的父对象TFiler对象中介绍过FBufPosFBufEnd变量。Filer对象内部分配了一个BufSize大小的缓冲区FBufPos就是指在缓冲区中的相对位置,FBufEnd是指在缓冲区中数据结束处的位置(缓冲区中的数据不一定会充满整个缓冲区)

本文来自织梦

 在GetPosition方法中可以看到Reader对象的Position值和Stream对象的Position值是不同的。Reader对象多了一个FButPos的编移量。

内容来自dedecms

  2. DefinepropertyDefineBinaryproperty方法的实现

织梦好,好织梦

这两个方法是虚方法,在TFiler中是抽象方法,在TReaderTWriter对象中才有具体的实现。 dedecms.com

  它们在TReader中的实现如下: 本文来自织梦

   本文来自织梦

procedure TReader.DefineProperty(const Name: string; ReadData: TReaderProc; 内容来自dedecms

WriteData: TWriterProc; HasData: Boolean);

dedecms.com

begin 内容来自dedecms

if CompareText(Name, FPropName) = 0 then dedecms.com

begin

本文来自织梦

ReadData(Self); dedecms.com

FPropName := ''; 内容来自dedecms

end;

dedecms.com

end;

本文来自织梦

  织梦好,好织梦

procedure TReader.DefineBinaryProperty(const Name: string;

织梦好,好织梦

ReadData, WriteData: TStreamProc; HasData: Boolean);

织梦好,好织梦

var

织梦内容管理系统

Stream: TMemoryStream; copyright dedecms

Count: Longint; 织梦内容管理系统

begin

本文来自织梦

if CompareText(Name, FPropName) = 0 then

内容来自dedecms

begin

copyright dedecms

if ReadValue <> vaBinary then copyright dedecms

begin dedecms.com

Dec(FBufPos);

织梦好,好织梦

SkipValue; dedecms.com

FCanHandleExcepts := True; dedecms.com

PropValueError;

内容来自dedecms

end;

织梦内容管理系统

Stream := TMemoryStream.Create;

dedecms.com

try copyright dedecms

Read(Count, SizeOf(Count)); dedecms.com

Stream.SetSize(Count);

内容来自dedecms

Read(Stream.Memory^, Count); 内容来自dedecms

FCanHandleExcepts := True;

本文来自织梦

ReadData(Stream);

织梦好,好织梦

finally 织梦好,好织梦

Stream.Free;

内容来自dedecms

end;

dedecms.com

FPropName := '';

本文来自织梦

end; copyright dedecms

end; copyright dedecms

  copyright dedecms

在两个方法都将Name参数值与当前的属性名比较,如果相同则进行读操作。在DefineBinaryproperty中,创建了一个内存流。先将数据读到内存流中然后调用ReadData读取数据。

织梦内容管理系统

  3. FlushBuffer的实现

内容来自dedecms

  FlushBuffer方法用于清除Reader对象的内部缓冲区中的内容,保持Reader对象和流在位置(Position)上的同步,其实现如下:

织梦好,好织梦

 

本文来自织梦

procedure TReader.FlushBuffer;

织梦内容管理系统

begin 本文来自织梦

FStream.Position := FStream.Position - (FBufEnd - FBufPos); 织梦好,好织梦

FBufPos := 0;

dedecms.com

FBufEnd := 0;

本文来自织梦

end; 本文来自织梦

 

copyright dedecms

  4. ReadListBeginReadListEndEndOfList方法 织梦内容管理系统

  这三个方法都是用于从Reader对象的流中读取一连串的项目,并且这些项目都由WriteListBegin写入的标志标定开始和WriteListEnd写入标志,标定结束,在读循环中用EndOfList进行判断。它们是在Reader对象读取流中数据时经常用于的。它们的实现如下: dedecms.com

  内容来自dedecms

procedure TReader.ReadListBegin; dedecms.com

begin

本文来自织梦

CheckValue(vaList); dedecms.com

end; 织梦内容管理系统

 

copyright dedecms

procedure TReader.ReadListEnd;

copyright dedecms

begin copyright dedecms

CheckValue(vaNull);

内容来自dedecms

end; copyright dedecms

  内容来自dedecms

function TReader.EndOfList: Boolean;

织梦内容管理系统

begin 内容来自dedecms

Result := ReadValue = vaNull; 内容来自dedecms

Dec(FBufPos);

dedecms.com

end;

织梦内容管理系统

 

织梦好,好织梦

  项目表开始标志是VaList,项目表结束标志是VaNullVaListVaNull都是枚举类型TValueType定义的常量。 copyright dedecms

  它们实现中调用的CheckValueTReader的私有方法,其实现如下: copyright dedecms

  copyright dedecms

procedure TReader.CheckValue(Value: TValueType);

织梦内容管理系统

begin 内容来自dedecms

if ReadValue <> Value then

本文来自织梦

begin

copyright dedecms

Dec(FBufPos); dedecms.com

SkipValue; 内容来自dedecms

PropValueError; 织梦内容管理系统

end;

本文来自织梦

end; 内容来自dedecms

 

本文来自织梦

  CheckValue方法的功能是检测紧接着要读的值是否是Value指定的类型。如果不是则跳过该项目并触发一个SInvalidPropertyValue错误。

织梦内容管理系统

  EndOfList函数只是简单地判断下一字节是否是VaNull将判断结果返回,并将字节移回原来位置。

织梦内容管理系统

  5. 简单数据类型读方法的实现

本文来自织梦

  简单数据类型指的是布尔型、字符型、整型、字符串型、浮点型、集合类型和标识符。将它们放在一起介绍是因为它们的实现方法类似。 dedecms.com

  因为它们的实现都用到了ReadValue方法,因此先来介绍ReadValue方法的实现:

dedecms.com

  织梦好,好织梦

function TReader.ReadValue: TValueType; 织梦内容管理系统

begin 内容来自dedecms

Read(Result, SizeOf(Result));

织梦内容管理系统

end; 内容来自dedecms

  本文来自织梦

  该方法调用私有方法Read,从Reader对象流中读一个字节,并移动位置指针。 织梦好,好织梦

  ReadValue方法专门从流中读取值的类型的,所有的数据读写方法中在读取数据前都要调用ReadValue方法判断是否是所要读的数据。如果是,则调用Read方法读取数据;否则触发一个异常事件,下面看Integer类型的读方法:

dedecms.com

 

织梦内容管理系统

function TReader.ReadInteger: Longint;

dedecms.com

var dedecms.com

S: Shortint; copyright dedecms

I: Smallint; copyright dedecms

begin

copyright dedecms

case ReadValue of

织梦好,好织梦

vaInt8:

dedecms.com

begin 织梦内容管理系统

Read(S, SizeOf(Shortint)); 织梦内容管理系统

Result := S; copyright dedecms

end; copyright dedecms

vaInt16:

织梦好,好织梦

begin

内容来自dedecms

Read(I, SizeOf(I));

本文来自织梦

Result := I;

织梦内容管理系统

end;

织梦内容管理系统

vaInt32:

内容来自dedecms

Read(Result, SizeOf(Result));

copyright dedecms

else

本文来自织梦

PropValueError; 内容来自dedecms

end;

dedecms.com

end;

本文来自织梦

 

内容来自dedecms

因为Delphi 2.0中,整型可分8位、16位和32位,因此读取整型数据时分别作了判断。

本文来自织梦

  布尔类型的数据是直接放在值类型标志上,如果类型为VaTrue,则值为True;如果类型为VaFalse,则值为False

dedecms.com

  织梦内容管理系统

function TReader.ReadBoolean: Boolean;

内容来自dedecms

begin 织梦好,好织梦

Result := ReadValue = vaTrue;

copyright dedecms

end;

内容来自dedecms

  织梦好,好织梦

ReadString方法也利用ReadValue方法判断是字符串还是长字符串。 织梦内容管理系统

  dedecms.com

function TReader.ReadString: string;

dedecms.com

var 织梦内容管理系统

L: Integer;

织梦好,好织梦

begin

dedecms.com

L := 0; 织梦好,好织梦

case ReadValue of

内容来自dedecms

vaString:

织梦内容管理系统

Read(L, SizeOf(Byte)); dedecms.com

vaLString: 内容来自dedecms

Read(L, SizeOf(Integer));

copyright dedecms

else

织梦内容管理系统

PropValueError; 本文来自织梦

end; 织梦内容管理系统

SetString(Result, PChar(nil), L); 织梦内容管理系统

Read(Pointer(Result)^, L); 内容来自dedecms

end; dedecms.com

 

织梦好,好织梦

如果VaString类型紧接着一个字节存有字符串的长度;如果是VaLString类,则紧接着两个字节存放字符串长度,然后根据字符串长度用SetString过程给分配空间,用Read方法读出数据。 内容来自dedecms

  ReadFloat方法允许将整型值转换为浮点型。 内容来自dedecms

  织梦内容管理系统

function TReader.ReadFloat: Extended;

织梦内容管理系统

begin copyright dedecms

if ReadValue = vaExtended then Read(Result, SizeOf(Result)) else 内容来自dedecms

begin 织梦内容管理系统

Dec(FBufPos);

dedecms.com

Result := ReadInteger; 本文来自织梦

end;

dedecms.com

end;

copyright dedecms

  内容来自dedecms

字符类型数据设有直接的标志,它是根据VaString后面放一个序值为1的字节来判断的。

本文来自织梦

 

织梦好,好织梦

function TReader.ReadChar: Char;

织梦内容管理系统

begin

内容来自dedecms

CheckValue(vaString);

内容来自dedecms

Read(Result, 1);

内容来自dedecms

if Ord(Result) <> 1 then copyright dedecms

begin 内容来自dedecms

Dec(FBufPos); 本文来自织梦

ReadStr; 织梦内容管理系统

PropValueError;

内容来自dedecms

end;

织梦好,好织梦

Read(Result, 1);

dedecms.com

end; 本文来自织梦

 

copyright dedecms

出于读取DFM文件需要,Filer对象支持读取标识符。

copyright dedecms

  copyright dedecms

function TReader.ReadIdent: string;

内容来自dedecms

var

织梦内容管理系统

L: Byte;

织梦内容管理系统

begin dedecms.com

case ReadValue of 本文来自织梦

vaIdent:

织梦内容管理系统

begin

dedecms.com

Read(L, SizeOf(Byte));

copyright dedecms

SetString(Result, PChar(nil), L);

copyright dedecms

Read(Result[1], L);

内容来自dedecms

end; 本文来自织梦

vaFalse: copyright dedecms

Result := 'False';

本文来自织梦

vaTrue: dedecms.com

Result := 'True'; dedecms.com

vaNil: 织梦内容管理系统

Result := 'nil'; 本文来自织梦

else dedecms.com

PropValueError;

copyright dedecms

end;

dedecms.com

end;

dedecms.com

  本文来自织梦

一般说来,各种复杂的数据结构都是由这些简单数据组成;定义了这些方法等于给读各种类型的数据提供了元操作,使用很方便。例如,读取字符串类型的数据时,如果采用传流方法还要判断字符串的长度,使用ReadString方法就不同了。但应该特别注意的是这些类型数据的存储格式是由Delphi设计的与简单数据类型有明显的不同。因此,存入数据时应当使用Writer对象相应的方法,而且在读数据前要用NextValue方法进行判断,否则会触发异常事件。 copyright dedecms

  6. 读取部件的方法的实现

内容来自dedecms

  Reader对象中用于读取部件的方法有ReadSignatureReadPrefixReadComponentReadRootComponentReadComponents

copyright dedecms

ReadSignature方法主要用于读取Delphi Filer对象标签一般在读取部件前,都要用调用ReadSignature方法以指导部件读写过程。 dedecms.com

  内容来自dedecms

procedure TReader.ReadSignature;

本文来自织梦

var dedecms.com

Signature: Longint;

织梦好,好织梦

begin 内容来自dedecms

Read(Signature, SizeOf(Signature)); dedecms.com

if Signature <> Longint(FilerSignature) then ReadError(SInvalidImage); dedecms.com

end; 内容来自dedecms

  内容来自dedecms

FilerSignature就是Filer对象标签其值为“TPF0 ,如果读的不是“TPF0 ,则会触发SInValidImage异常事件。

织梦好,好织梦

  ReadPrefix方法是用于读取流中部件前的标志位,该标志表示该部件是否处于从祖先窗体中继承的窗体中和它在窗体中的位置是否很重要。 dedecms.com

  织梦好,好织梦

procedure TReader.ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer);

dedecms.com

var 织梦内容管理系统

Prefix: Byte;

dedecms.com

begin copyright dedecms

Flags := [];

dedecms.com

if Byte(NextValue) and $F0 = $F0 then

织梦好,好织梦

begin

织梦内容管理系统

Prefix := Byte(ReadValue);

织梦内容管理系统

Byte(Flags) := Prefix and $0F;

dedecms.com

if ffChildPos in Flags then AChildPos := ReadInteger;

织梦好,好织梦

end;

copyright dedecms

end; 织梦内容管理系统

  dedecms.com

  TFilerFlags的定义是这样的:

内容来自dedecms

  本文来自织梦

   TFilerFlag = (ffInherited, ffChildPos);

织梦好,好织梦

TFilerFlags = Set of TFilerFlag;

copyright dedecms

  织梦内容管理系统

充当标志的字节的高四位是$F,低四位是集合的值,也是标志位的真正含义。如果ffChildPos置位,则紧接着的整型数字中放着部件在窗体中的位置序值。

织梦好,好织梦

  ReadComponent方法用于从Reader对象的流中读取部件。Component 参数指定了要从流中读取的对象。函数返回所读的部件。

copyright dedecms

  内容来自dedecms

function TReader.ReadComponent(Component: TComponent): TComponent; 织梦好,好织梦

var 本文来自织梦

CompClass, CompName: string; dedecms.com

Flags: TFilerFlags; 织梦内容管理系统

Position: Integer;

本文来自织梦

内容来自dedecms

 

织梦内容管理系统

begin

内容来自dedecms

ReadPrefix(Flags, Position);

织梦好,好织梦

CompClass := ReadStr;

内容来自dedecms

CompName := ReadStr;

织梦好,好织梦

Result := Component;

织梦内容管理系统

if Result = nil then

本文来自织梦

if ffInherited in Flags then

copyright dedecms

FindExistingComponent else

copyright dedecms

CreateComponent; dedecms.com

if Result <> nil then

copyright dedecms

try 本文来自织梦

Include(Result.FComponentState, csLoading);

copyright dedecms

if not (ffInherited in Flags) then SetCompName; copyright dedecms

if Result = nil then Exit;

织梦好,好织梦

Include(Result.FComponentState, csReading); 织梦好,好织梦

Result.ReadState(Self); 织梦内容管理系统

Exclude(Result.FComponentState, csReading);

本文来自织梦

if ffChildPos in Flags then Parent.SetChildOrder(Result, Position);

织梦好,好织梦

FLoaded.Add(Result);

dedecms.com

except 织梦内容管理系统

if ComponentCreated then Result.Free; 织梦内容管理系统

raise;

copyright dedecms

end;

织梦好,好织梦

end;

本文来自织梦

  copyright dedecms

ReadCompontent方法首先调用ReadPrefix方法,读出部件标志位和它的创建次序值(Create Order)。然后用ReadStr方法分别读出部件类名和部件名。如果Component参数为nil,则执行两个任务:

dedecms.com

如果ffInberited 置位则从Root 找已有部件,否则,就从系统的Class表中找到该部件类型的定义并创建 copyright dedecms

如果结果不为空,将用部件的ReadState方法读入各种属性值,并设置部件的Parent 属性,并恢复它在Parent部件的创建次序。 内容来自dedecms

 

本文来自织梦

  ReadComponent方法主要是调用ReadComponent方法从Reader对象的流中读取一连串相关联的部件,并分解相互引用关系。 copyright dedecms

 

本文来自织梦

procedure TReader.ReadComponents(AOwner, AParent: TComponent;

织梦内容管理系统

Proc: TReadComponentsProc); 内容来自dedecms

var 本文来自织梦

Component: TComponent;

织梦好,好织梦

begin

内容来自dedecms

Root := AOwner; 织梦内容管理系统

Owner := AOwner;

织梦内容管理系统

Parent := AParent; 织梦好,好织梦

BeginReferences; copyright dedecms

try dedecms.com

while not EndOfList do

织梦好,好织梦

begin

织梦好,好织梦

ReadSignature;

织梦好,好织梦

Component := ReadComponent(nil);

织梦内容管理系统

Proc(Component);

织梦内容管理系统

end;

copyright dedecms

FixupReferences;

织梦好,好织梦

finally

copyright dedecms

EndReferences; dedecms.com

end; 织梦内容管理系统

end; dedecms.com

  copyright dedecms

  ReadComponents首先用AOwnerAParent参数给Root,OwnerParent赋值,用于重建各部件的相互引用。然后用一个While循环读取部件并用由Proc传入的方法进行处理。在重建引用关系时,用了BeginReferencesFixUpReferencesEndReferences嵌套模式。 本文来自织梦

  ReadRootComponent方法从Reader对象的流中将部件及其拥有的部件全部读出。如果Component参数为nil,则创建一个相同类型的部件,最后返回该部件: dedecms.com

  dedecms.com

function TReader.ReadRootComponent(Root: TComponent): TComponent;

内容来自dedecms

  织梦好,好织梦

function FindUniqueName(const Name: string): string; 内容来自dedecms

begin

本文来自织梦

dedecms.com

end; 织梦好,好织梦

  织梦内容管理系统

var

织梦好,好织梦

I: Integer;

织梦内容管理系统

Flags: TFilerFlags; dedecms.com

begin 内容来自dedecms

ReadSignature; 内容来自dedecms

Result := nil;

dedecms.com

try

织梦内容管理系统

ReadPrefix(Flags, I); copyright dedecms

if Root = nil then 本文来自织梦

begin

织梦好,好织梦

Result := TComponentClass(FindClass(ReadStr)).Create(nil); dedecms.com

Result.Name := ReadStr; 本文来自织梦

end else dedecms.com

begin 织梦内容管理系统

Result := Root; 织梦好,好织梦

ReadStr; { Ignore class name } 内容来自dedecms

if csDesigning in Result.ComponentState then

dedecms.com

ReadStr else

内容来自dedecms

Result.Name := FindUniqueName(ReadStr); 本文来自织梦

end; 织梦内容管理系统

FRoot := Result;

内容来自dedecms

if GlobalLoaded <> nil then

copyright dedecms

FLoaded := GlobalLoaded else

织梦内容管理系统

FLoaded := TList.Create; copyright dedecms

try

织梦内容管理系统

FLoaded.Add(FRoot); 织梦好,好织梦

FOwner := FRoot; 本文来自织梦

Include(FRoot.FComponentState, csLoading); 内容来自dedecms

Include(FRoot.FComponentState, csReading);

织梦好,好织梦

FRoot.ReadState(Self); dedecms.com

Exclude(FRoot.FComponentState, csReading); dedecms.com

if GlobalLoaded = nil then copyright dedecms

for I := 0 to FLoaded.Count - 1 do TComponent(FLoaded[I]).Loaded; 织梦好,好织梦

finally

织梦内容管理系统

if GlobalLoaded = nil then FLoaded.Free;

内容来自dedecms

FLoaded := nil; 内容来自dedecms

end;

本文来自织梦

GlobalFixupReferences;

dedecms.com

except

本文来自织梦

RemoveFixupReferences(Root, ''); 织梦好,好织梦

if Root = nil then Result.Free; 本文来自织梦

raise;

本文来自织梦

end; 织梦好,好织梦

end; 织梦内容管理系统

  dedecms.com

  ReadRootComponent首先调用ReadSignature读取Filer对象标签。然后在tryexcept循环中执行读取任务。如果Root参数为nil,则用ReadStr读出的类名创建新部件,并以流中读出部件的Name属性;否则,忽略类名,并判断Name属性的唯一性。最后用RootReadState方法读取属性和其拥有的拥有并处理引用关系。

copyright dedecms

  7. SetName方法和OnSetName事件

内容来自dedecms

  因为在OnSetName事件中,Name参数是var型的,所以可以用OnSetName事件处理过程修改所读部件的名字。而OnSetName事件处理过程是在SetName方法中实现的。

copyright dedecms

  织梦内容管理系统

procedure TReader.SetName(Component: TComponent; var Name: string);

copyright dedecms

begin copyright dedecms

if Assigned(FOnSetName) then FOnSetName(Self, Component, Name); 织梦好,好织梦

Component.Name := Name;

织梦好,好织梦

end;

copyright dedecms

  内容来自dedecms

SetName方法和OnSetName事件在动态DFM文件的编程中有很重要的作用。 copyright dedecms

  8. TReader的错误处理

copyright dedecms

  TReader的错误处理是由Error方法和OnError事件的配合使用完成的。OnError 事件处理过程的Handled参数是var型的布尔变量,通过将Handled设为TrueFalse可影响TReader 的错误处理。OnError事件处理过程是在Error方法中调用的。 织梦内容管理系统

  dedecms.com

function TReader.Error(const Message: string): Boolean; dedecms.com

begin

织梦内容管理系统

Result := False; copyright dedecms

if Assigned(FOnError) then FOnError(Self, Message, Result); copyright dedecms

end;

织梦内容管理系统

 

织梦好,好织梦

  9. FindMethodOnFindMethod事件 织梦好,好织梦

  有时,在程序运行期间,给部件的方法指针(主要是事件处理过程)动态赋值是很有用的,这样就能动态地改变部件响应事件的方式。在流中读取部件捍做到一点就要利用OnFindMehtod事件。OnFIndMethod事件是在FindMethod方法中被调用的。 织梦内容管理系统

  dedecms.com

function TReader.FindMethod(Root: TComponent;

内容来自dedecms

const MethodName: string): Pointer; 本文来自织梦

var dedecms.com

Error: Boolean;

本文来自织梦

begin 内容来自dedecms

Result := Root.MethodAddress(MethodName);

copyright dedecms

Error := Result = nil;

内容来自dedecms

if Assigned(FOnFindMethod) then FOnFindMethod(Self, MethodName, Result, dedecms.com

Error); 本文来自织梦

if Error then PropValueError;

copyright dedecms

end;

织梦内容管理系统

 

内容来自dedecms

  OnFindMethod 方法除了可以给部件的MethodName所指定的方法指针动态赋值外,还可修改Error参数来决定是否处理Missing Method错误。方法中调用的MehtodAddress 方法定义在TObject中,它是个很有用的方法,它可以得到对象中定义的public方法的地址。FindMethod方法和OnFindMethod事件在动态DFM的编程中有很重要的作用。

dedecms.com

 

内容来自dedecms

 

织梦内容管理系统

20.3 Delphi对象式数据管理应用实例

织梦好,好织梦

  dedecms.com

  Delphi 2.0无论是其可视化设计工具,还是可视化部件类库(VCL),都处处渗透了对象存储技术,本节将从Delphi可视化设计内部机制、VCL中的数据存储、BLOB数据操作和动态生成部件的存储几方面介绍对象存储功能的实例应用。 copyright dedecms

  dedecms.com

20.3.1 Delphi 动态DFM文件及部件的存取在超媒体系统中的应用 织梦内容管理系统

 

本文来自织梦

  Delphi的可视化设计工具是同其部件类库紧密结合在一起的。

dedecms.com

  每个部件只有通过一段注册程序并通过DelphiInstall Component功能,才能出现在Component Palette上;部件的属性才有可能出现在Object Inspector窗口中;部件的属性编辑器才能被Delphi环境使用。因为这种浑然天成的关系,DFM文件存取必然得到VCL在程序上的支持。

内容来自dedecms

  DFM文件的部件存取是Delphi可视化设计环境中文件存取的中心问题。因为Delphi可视化设计的核心是窗体的设计。每个窗体对应一个库单元,是应用程序的模块,窗体在磁盘上的存储就是DFM文件。 织梦好,好织梦

  DFM文件结构我们前面介绍过了。它实际上是存储窗体及其拥有的所有部件的属性。这种拥有关系是递归的。问题在于如何将这些属性数据与程序中的变量(属性)代码联系起来。

本文来自织梦

  在Delphi中处理这种联系的过程分为两种情况:设计时和运行时。

织梦好,好织梦

在设计时,建立联系表现为读取DFM 文件,建立DFM文件中的部件及其属性与可视化设计工具(Object Inspector、窗体设计窗口和代码编辑器)的联系,也就是说让这些部件及其属性能出现在这些窗口中,并与代码中的属性定义联系起来;分解联系表现为存储DFM文件,将窗体窗口中的部件及其属性写入DFM文件。 织梦内容管理系统

在运行时,主要是建立联系的过程,即读取DFM文件。这时,DFM文件不是作为独立的磁盘文件,而是以应用程序资源中的RCDATA类型的二进制数据存在。建立联系的过程表现为将资源中的部件及其属性与应用程序中的对象及其数据域联系起来。其过程为:根据DFM中的部件类名创建对象,再将用DFM中的部件属性值给程序中的部件属性赋值。当然要完成这一过程,还必须在代码中有相应的窗体定义,因为方法等代码是不存入部件的。 copyright dedecms

  VCL对读取DFM文件在代码上的支持是通过Stream对象和Filer对象达到的。在20. 120.1节中,我们可以看到Stream对象和Filer对象中有大量的用于存取部件及其属性的方法,尤其在TReader对象中,还有关于错误处理和动态的方法赋值的方法。下面我们就通过程序实例介绍存取DFM文件方法、步骤和注意事项。

本文来自织梦

精彩推荐
热点内容
最近更新