treader,twriter做什么用? ( 积分: 100 )

  • 主题发起人 主题发起人 xmlshell
  • 开始时间 开始时间
X

xmlshell

Unregistered / Unconfirmed
GUEST, unregistred user!
对这两个东西很好奇,想知道能有什么用途。
treader可以实现运行时动态的读取一个窗体,并运行这个窗口吗?
 
对这两个东西很好奇,想知道能有什么用途。
treader可以实现运行时动态的读取一个窗体,并运行这个窗口吗?
 
其它、TReader

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

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

 

一.属性和方法

1. Owner属性  声明:property Owner: TComponent


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

2. Parent属性  声明:property Parent: TComponent


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

3. Position属性  声明:propertion: Longint


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

4. BeginReferences方法  声明:procedure BeginReferences


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

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

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

 

BeginReferences
{ 创建临时列表 }

try

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



FixupReferences
{ 分 解 }

finally

EndReferences
{ 释放临时列表 }

end;

 

5. FixUpReferences方法  声明:procedure FixupReferences


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

6. EndReferences方法  声明:procedure EndReferences


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

7. ReadListBegin方法  声明:procedure ReadListBegin


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

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

8. ReadListEnd方法  声明:procedure ReadListEnd


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

9. EndOfList方法  声明:function EndOfList: Boolean


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

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

 

procedure TStrings.ReadData(Reader: TReader);

begin

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

Clear
{ 清除已有的字符串 }

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

Add(Reader.ReadString)
{ …读一个字符串并将其加在列表中 }

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

end;

 

10. ReadSignature方法  声明:procedure ReadSignature


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

11. ReadPrefix方法  声明:procedure ReadPrefix(var Plags: TFilerFlags
var AChild, Pos: Integer)


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

12. OnFindMethod事件  声明:property OnFindMethod: TFindMethodEvent


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

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

13. Error方法  声明:function Error(const Message: String): Boolean
virtual


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

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

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

  try

… { 读部件 }

except

on E: Exception do

begin

…{ 执行一些清除操作 }

if Error(E.Message) then raise


end


end;

 

14. OnError事件  声明:property OnError: TReaderError


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

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

 

15. SetName方法

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


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

16. OnSetName事件  声明:property OnSetName: TSetNameEvent


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

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

 

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

var Name: string);

var

ButtonPos: Integer;

begin

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

if ButtonPos <> 0 then

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

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

end;

 

17. ReadValue方法  声明:function ReadValue: TValueType


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

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

18. NextValue方法  声明:function Nextvalue: TValuetype


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

19. ReadBoolean方法  声明:function ReadBoolean: Boolean


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

20、ReadChar方法  声明:function ReadChar: char


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

21. ReadFloat方法  声明:function ReadFloat: Extended


  ReadFloat方法从流中读取浮点数。

20. ReadIdent方法  声明:function ReadIdent: string


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

23. ReadInteger方法  声明:function ReadInteger: Longin

ReadInteger方法从流中读取整型数字。

24.ReadString方法  声明:function Read String: string


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

 









二.实现

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

1. TReader属性的实现

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

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

 

TReader = class(TFiler)

private



function GetPosition: Longint;

procedure SetPosition(Value: Longint);

public



property Position: Longint read GetPosition write SetPosition;

end;

 

Postition的读写控制方法如下:

 

function TReader.GetPosition: Longint;

begin

Result := FStream.Position + FBufPos;

end;

 

procedure TReader.SetPosition(Value: Longint);

begin

FStream.Position := Value;

FBufPos := 0;

FBufEnd := 0;

end;

 

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

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

2. Defineproperty和DefineBinaryproperty方法的实现

这两个方法是虚方法,在TFiler中是抽象方法,在TReader和TWriter对象中才有具体的实现。

  它们在TReader中的实现如下:

  

procedure TReader.DefineProperty(const Name: string
ReadData: TReaderProc


WriteData: TWriterProc
HasData: Boolean);

begin

if CompareText(Name, FPropName) = 0 then

begin

ReadData(Self);

FPropName := '';

end;

end;

 

procedure TReader.DefineBinaryProperty(const Name: string;

ReadData, WriteData: TStreamProc
HasData: Boolean);

var

Stream: TMemoryStream;

Count: Longint;

begin

if CompareText(Name, FPropName) = 0 then

begin

if ReadValue <> vaBinary then

begin

Dec(FBufPos);

SkipValue;

FCanHandleExcepts := True;

PropValueError;

end;

Stream := TMemoryStream.Create;

try

Read(Count, SizeOf(Count));

Stream.SetSize(Count);

Read(Stream.Memory^, Count);

FCanHandleExcepts := True;

ReadData(Stream);

finally

Stream.Free;

end;

FPropName := '';

end;

end;

 

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

3. FlushBuffer的实现

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

 

procedure TReader.FlushBuffer;

begin

FStream.Position := FStream.Position - (FBufEnd - FBufPos);

FBufPos := 0;

FBufEnd := 0;

end;

 

4. ReadListBegin、ReadListEnd和EndOfList方法

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

 

procedure TReader.ReadListBegin;

begin

CheckValue(vaList);

end;

 

procedure TReader.ReadListEnd;

begin

CheckValue(vaNull);

end;

 

function TReader.EndOfList: Boolean;

begin

Result := ReadValue = vaNull;

Dec(FBufPos);

end;

 

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

  它们实现中调用的CheckValue是TReader的私有方法,其实现如下:

procedure TReader.CheckValue(Value: TValueType);

begin

if ReadValue <> Value then

begin

Dec(FBufPos);

SkipValue;

PropValueError;

end;

end;

 

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

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

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

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

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

function TReader.ReadValue: TValueType;

begin

Read(Result, SizeOf(Result));

end


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

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

function TReader.ReadInteger: Longint;

var

S: Shortint;

I: Smallint;

begin

case ReadValue of

vaInt8:

begin

Read(S, SizeOf(Shortint));

Result := S;

end;

vaInt16:

begin

Read(I, SizeOf(I));

Result := I;

end;

vaInt32:

Read(Result, SizeOf(Result));

else

PropValueError;

end;

end;

 

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

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

function TReader.ReadBoolean: Boolean;

begin

Result := ReadValue = vaTrue;

end;

 

ReadString方法也利用ReadValue方法判断是字符串还是长字符串。

 

function TReader.ReadString: string;

var

L: Integer;

begin

L := 0;

case ReadValue of

vaString:

Read(L, SizeOf(Byte));

vaLString:

Read(L, SizeOf(Integer));

else

PropValueError;

end;

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

Read(Pointer(Result)^, L);

end;

 

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

  ReadFloat方法允许将整型值转换为浮点型。

 

function TReader.ReadFloat: Extended;

begin

if ReadValue = vaExtended then Read(Result, SizeOf(Result)) else

begin

Dec(FBufPos);

Result := ReadInteger;

end;

end;

 

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

 

function TReader.ReadChar: Char;

begin

CheckValue(vaString);

Read(Result, 1);

if Ord(Result) <> 1 then

begin

Dec(FBufPos);

ReadStr;

PropValueError;

end;

Read(Result, 1);

end;

 

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

 

function TReader.ReadIdent: string;

var

L: Byte;

begin

case ReadValue of

vaIdent:

begin

Read(L, SizeOf(Byte));

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

Read(Result[1], L);

end;

vaFalse:

Result := 'False';

vaTrue:

Result := 'True';

vaNil:

Result := 'nil';

else

PropValueError;

end;

end;

 

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

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

  Reader对象中用于读取部件的方法有ReadSignature、ReadPrefix、ReadComponent、ReadRootComponent和ReadComponents。

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

 

procedure TReader.ReadSignature;

var

Signature: Longint;

begin

Read(Signature, SizeOf(Signature));

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

end;

 

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

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

 

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

var

Prefix: Byte;

begin

Flags := [];

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

begin

Prefix := Byte(ReadValue);

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

if ffChildPos in Flags then AChildPos := ReadInteger;

end;

end;

 

TFilerFlags的定义是这样的:

TFilerFlag = (ffInherited, ffChildPos)


TFilerFlags = Set of TFilerFlag


 

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

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

 

function TReader.ReadComponent(Component: TComponent): TComponent;

var

CompClass, CompName: string;

Flags: TFilerFlags;

Position: Integer;



 

begin

ReadPrefix(Flags, Position);

CompClass := ReadStr;

CompName := ReadStr;

Result := Component;

if Result = nil then

if ffInherited in Flags then

FindExistingComponent else

CreateComponent;

if Result <> nil then

try

Include(Result.FComponentState, csLoading);

if not (ffInherited in Flags) then SetCompName;

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);

except

if ComponentCreated then Result.Free;

raise;

end;

end;

 

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

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

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

 

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

 

procedure TReader.ReadComponents(AOwner, AParent: TComponent;

Proc: TReadComponentsProc);

var

Component: TComponent;

begin

Root := AOwner;

Owner := AOwner;

Parent := AParent;

BeginReferences;

try

while not EndOfList do

begin

ReadSignature;

Component := ReadComponent(nil);

Proc(Component);

end;

FixupReferences;

finally

EndReferences;

end;

end;

  ReadComponents首先用AOwner和AParent参数给Root,Owner和Parent赋值,用于重建各部件的相互引用。然后用一个While循环读取部件并用由Proc传入的方法进行处理。在重建引用关系时,用了BeginReferences、FixUpReferences和EndReferences嵌套模式。

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

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

 

function FindUniqueName(const Name: string): string;

begin



end;

 

var

I: Integer;

Flags: TFilerFlags;

begin

ReadSignature;

Result := nil;

try

ReadPrefix(Flags, I);

if Root = nil then

begin

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

Result.Name := ReadStr;

end else

begin

Result := Root;

ReadStr
{ Ignore class name }

if csDesigning in Result.ComponentState then

ReadStr else

Result.Name := FindUniqueName(ReadStr);

end;

FRoot := Result;

if GlobalLoaded <> nil then

FLoaded := GlobalLoaded else

FLoaded := TList.Create;

try

FLoaded.Add(FRoot);

FOwner := FRoot;

Include(FRoot.FComponentState, csLoading);

Include(FRoot.FComponentState, csReading);

FRoot.ReadState(Self);

Exclude(FRoot.FComponentState, csReading);

if GlobalLoaded = nil then

for I := 0 to FLoaded.Count - 1 do TComponent(FLoaded).Loaded;

finally

if GlobalLoaded = nil then FLoaded.Free;

FLoaded := nil;

end;

GlobalFixupReferences;

except

RemoveFixupReferences(Root, '');

if Root = nil then Result.Free;

raise;

end;

end;

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

7. SetName方法和OnSetName事件

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

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

begin

if Assigned(FOnSetName) then FOnSetName(Self, Component, Name);

Component.Name := Name;

end;

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

8. TReader的错误处理

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

 

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

begin

Result := False;

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

end;

9. FindMethod和OnFindMethod事件

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

function TReader.FindMethod(Root: TComponent;

const MethodName: string): Pointer;

var

Error: Boolean;

begin

Result := Root.MethodAddress(MethodName);

Error := Result = nil;

if Assigned(FOnFindMethod) then FOnFindMethod(Self, MethodName, Result,

Error);

if Error then PropValueError;

end;

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

TWriter 对象是可实例化的,往流中写数据的Filer对象。TWriter对象直接从TFiler继承而来,除了覆盖从TFiler继承的方法外,还增加了大量的关于写各种数据类型(如Integer、String和Component等)的方法。TWriter对象和TReader 对象配合使用将使对象读写发挥巨大作用。

 

一.属性和方法

1. Position属性  声明:property Position: Longint


TWriter对象的Position属性表示相关联的流中的当前要写的位置,TReader 对象也有这个属性,但与TReader对象不同的是TWriter对象的Position的值比流的Position值小,这一点一看属性实现就清楚了。

2. RootAncesstor属性  声明:property RootAncestor: TComponent


RootAncestor属性表示的是Root属性所指的部件的祖先。如果Root 是继承的窗体,Writer对象将窗体拥有部件与祖先窗体中的相应部件依次比较,然后只写入那些与祖先中的不同的部件。

3. Write方法  声明:procedure Write(const Buf
Count: Longint)


Write方法从Buf中往与Writer相关联的流中写入Count个字节。

4. WriteListBegin方法  声明:procedure WriteListBegin


WriteListBegin方法往Write对象的流中写入项目列表开始标志,该标志意味着后面存储有一连串的项目。Reader对象,在读这一连串项目时先调用ReadListBegin方法读取该标志位,然后用EndOfList判断是否列表结束,并用循环语句读取项目。在调用WriteListBegin方法的后面必须调用WriteListEnd方法写列表结束标志,相应的在Reader对象中有ReadListEnd方法读取该结束标志。

5. WriteListEnd方法  声明:procedure WriteListEnd


WriteListEnd方法在流中,写入项目列表结束标志,它是与WriteListBegin相匹配的方法。

6. WriteBoolean方法  声明:procedure WriteBoolean(Value: Boolean)


WriteBoolean方法将Value传入的布尔值写入流中。

7. WriteChar方法  声明:procedure WriteChar(Value: char)


WriteChar方法将Value中的字符写入流中。

8. WriteFloat方法  声明:procedure WriteFloat(Value: Extended)


WriteFloat方法将Value传入的浮点数写入流中。

9. WriteInteger方法  声明:procedure WriteInteger(Value: Longint)


WriteInteger方法将Value中的整数写入流中。

10. WriteString方法  声明:procedure WriteString(const Value: string)


WriteString方法将Value中的字符串写入流中。

11. WriteIdent方法  声明:procedure WriteIdent(const Ident: string)


WriteIdent方法将Ident传入的标识符写入流中。

12. WriteSignature方法  声明:procedure WriteSignature


WriteSignature方法将Delphi Filer对象标签写入流中。WriteRootComponent方法在将部件写入流之前先调用WriteSignature方法写入Filer标签。Reader对象在读部件之前调用ReadSignature方法读取该标签以指导读操作。

13. WritComponent方法  声明:procedure WriteComponent(Component: TComponent)


WriteComponent方法调用参数Component的WriteState方法将部件写入流中。在调用WriteState之前,WriteComponent还将Component的ComponetnState属性置为csWriting。当WriteState返回时再清除csWriting.

14. WriteRootComponent方法

  声明:procedure WriteRootComponent(Root: TComponent)


WriteRootComponent方法将Writer对象Root属性设为参数Root带的值,然后调用WriteSignature方法往流中写入Filer对象标签,最后调用WriteComponent方法在流中存储Root部件。





二.对象的实现

  TWriter对象提供了许多往流中写各种类型数据的方法,这对于程序员来说是很重要的功能。TWrite对象往流中写数据是依据不同的数据采取不同的格式的。 因此要掌握TWriter对象的实现和应用方法,必须了解Writer对象存储数据的格式。

  首先要说明的是,每个Filer对象的流中都包含有Filer对象标签。该标签占四个字节其值为“TPF0”。Filer对象为WriteSignature和ReadSignature方法存取该标签。该标签主要用于Reader对象读数据(部件等)时,指导读操作。

  其次,Writer对象在存储数据前都要留一个字节的标志位,以指出后面存放的是什么类型的数据。该字节为TValueType类型的值。TValueType是枚举类型,占一个字节空间,其定义如下:

 TValueType = (VaNull, VaList, VaInt8, VaInt16, VaInt32, VaEntended, VaString, VaIdent, VaFalse, VaTrue, VaBinary, VaSet, VaLString, VaNil, VaCollection)


 

因此,对Writer对象的每一个写数据方法,在实现上,都要先写标志位再写相应的数据;而Reader对象的每一个读数据方法都要先读标志位进行判断,如果符合就读数据,否则产生一个读数据无效的异常事件。VaList标志有着特殊的用途,它是用来标识后面将有一连串类型相同的项目,而标识连续项目结束的标志是VaNull。因此,在Writer对象写连续若干个相同项目时,先用WriteListBegin写入VaList标志,写完数据项目后,再写出VaNull标志;而读这些数据时,以ReadListBegin开始,ReadListEnd结束,中间用EndofList函数判断是否有VaNull标志。

  下面就介绍它们的实现。

1. TWriter对象属性的实现

  TWriter对象直接从TFiler对象继承,它只增加了Position和RootAncestor属性。

RootAncestor属性在private部分有数据域FRootAncestor存入其值。在属性定义的读与控制上都是直接读取该值。

  Position属性的定义中包含了两个读写控制方法:GetPosition和SetPosition:

 

TWriter = class(TFiler)

private

FRootAncestor: TComponent;



function GetPosition: Longint;

procedure SetPosition(Value: Longint);

public



property Position: Longint read GetPosition write SetPosition;

property RootAncestor: TComponent read FRootAncestor write FRootAncestor;

end;

 

GetPosition和SetPosition方法实现如下:

 

function TWriter.GetPosition: Longint;

begin

Result := FStream.Position + FBufPos;

end;

 

procedure TWriter.SetPosition(Value: Longint);

var

StreamPosition: Longint;

begin

StreamPosition := FStream.Position;

{ 只清除越界的缓冲区 }

if (Value < StreamPosition) or (Value > StreamPosition + FBufPos) then

begin

WriteBuffer;

FStream.Position := Value;

end

else FBufPos := Value - StreamPosition;

end


 

  WriteBuffer是TWriter对象定义的私有方法,它的作用是将Writer 对象内部缓冲区中的有效数据写入流中,并将FBufPos置为0。Writer对象的FlushBuffer对象就是用WriteBuffer方法刷新缓冲区。

  在SetPosition方法中,如果Value值超出了边界(FStream.Position,FStream.Position + FBufPos),就将缓冲区中的内容写入流,重新设置缓冲区在流中的相对位置;否则,就只是移动FBufPos指针。

2. TWriter方法的实现

  ⑴ WriteListBegin和WriteListEnd的实现

  这两个方法都是用于写连续若干个相同类型的值。WriteListBegin写入VaList标志,WriteListEnd写入VaNull标志。

 

procedure TWriter.WriteListBegin;

begin

WriteValue(vaList);

end;

 

procedure TWriter.WriteListEnd;

begin

WriteValue(vaNull);

end;

 

  这两个方法都调用TWriter对象的WriteValue方法,该方法主要用于写入TValueType类型的值。

 

procedure TWriter.WriteValue(Value: TValueType);

begin

Write(Value, SizeOf(Value))


end;

 

  ⑵ 简单数据类型的写入

  简单数据类型指的是整型、字符型、字符串型、浮点型、布尔型等。TWriter对象都定义了相应的写入方法。

  WriteInteger方法用于写入整型数据。

 

procedure TWriter.WriteInteger(Value: Longint);

begin

if (Value >= -128) and (Value <= 127) then

begin

WriteValue(vaInt8);

Write(Value, SizeOf(Shortint));

end else

if (Value >= -32768) and (Value <= 32767) then

begin

WriteValue(vaInt16);

Write(Value, SizeOf(Smallint));

end else

begin

WriteValue(vaInt32);

Write(Value, SizeOf(Longint));

end;

end;

 

  WriteInteger方法将整型数据分为8位、16位和32位三种,并分别用vaInt8、vaInt16和VaInt32。

  WriteBoolean用于写入布尔型数据:

 

procedure TWriter.WriteBoolean(Value: Boolean);

begin

if Value then

WriteValue(vaTrue) else

WriteValue(vaFalse);

end;

 

  与其它数据类型不同的是布尔型数据只使用了标志位是存储布尔值,在标志位后没有数据。

  WriteFloat方法用于写入浮点型数据。

 

procedure TWriter.WriteFloat(Value: Extended);

begin

WriteValue(vaExtended);

Write(Value, SizeOf(Extended));

end;

 

  字符串“True”、“False”和“nil”作为标识符传入是由于Delphi的特殊需要。如果是“True”、“False”和“nil”则写入VaTrue、VaFalse和VaNil,否则写入VaIdent标志,接着以字符串形式写入标识符。

  WriteString方法用于写入字符串

  

procedure TWriter.WriteString(const Value: string);

var

L: Integer;

begin

L := Length(Value);

if L <= 255 then

begin

WriteValue(vaString);

Write(L, SizeOf(Byte));

end else

begin

WriteValue(vaLString);

Write(L, SizeOf(Integer));

end;

Write(Pointer(Value)^, L);

end;

 

  Delphi的字符串类型有两种。一种长度小于256个字节,另一种长度小于65536 个字节。WriteString方法区分这两类情况存储字符串,一种设置VaStirng标志,另一种设置VaLString。然后存储字符串的长度值,最后存储字符串数据。

  WriteChar方法用于写入字符。

  

procedure TWriter.WriteChar(Value: Char);

begin

WriteString(Value);

end;

 

  字符类型的读写是用读写字符串的方法,在读的时候,判断字节数为1时,则为字符型。

  ⑶ 部件的写入

  TWriter对象中与写入部件有关的方法有WriteSignature、WritePrefix、WriteComponent、WriteDescendant和WriteRootComponent。

 WriteSignature方法用于往流中写入Filer对象标签。

 

procedure TWriter.WriteSignature;

begin

Write(FilerSignature, SizeOf(FilerSignature));

end;

 

  FilerStgnature是字符串常量,其值为“TPF0”,代表对象标签。

  WritePrefix方法用于在写入部件前写入ffInherited和ffChildPos标志,这些标志表示部件的继承特征和创建序值特征。

 

procedure TWriter.WritePrefix(Flags: TFilerFlags
AChildPos: Integer);

var

Prefix: Byte;

begin

if Flags <> [] then

begin

Prefix := $F0 or Byte(Flags);

Write(Prefix, SizeOf(Prefix));

if ffChildPos in Flags then WriteInteger(AChildPos);

end;

end;

 

  如果ffChildPos置位,则存入部件在Owner中的创建序值。更详细的信息请参阅TReader的ReadPrefix方法。

  WriteComponent方法往流中写入部件。

 

procedure TWriter.WriteComponent(Component: TComponent);

 

function FindAncestor(const Name: string): TComponent;

begin



end;

 

begin

Include(Component.FComponentState, csWriting);

if Assigned(FAncestorList) then

Ancestor := FindAncestor(Component.Name);

Component.WriteState(Self);

Exclude(Component.FComponentState, csWriting);

end;

 

  方法中用Component的WritState方法写入部件的属性。在写入之前将Component.FComponentState置为csWriting写入完后再将csWriting复位。

  WriteDescendant是根据祖先AAncestor的情况写入部件Root。

 

procedure TWriter.WriteDescendent(Root: TComponent
AAncestor: TComponent);

begin

FRootAncestor := AAncestor;

FAncestor := AAncestor;

FRoot := Root;

WriteSignature;

WriteComponent(Root);

end;

 

方法先调用WriteSignature方法写入Filer对象标签。然后调用WriteComponent将部件Root写入流。

  WriteRootComponent方法则是调用WriteDescendant方法写入部件,只是将后者的Ancestor参数以nil值传入。 

procedure TWriter.WriteRootComponent(Root: TComponent);

begin

WriteDescendent(Root, nil);

end;
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2421751

Delphi 的持续机制浅探

看看就明白了
 
谢谢
我的想法是这样的,
主程序运行时动态的读取一个子窗体,并运行这个子窗体,
类似pb把datawindow存储在数据库中,然后根据需要来调用。
请问各位大侠在delphi中如何实现??
 
http://www.delphibbs.com/keylife/iblog_show.asp?xid=14383
KeyLife富翁笔记
作者?: wuchunhua
标题?: Delphi的&quot;动态窗体&quot;技术实际应用 (抄)

另外可以找Stream的ReadComponent

分不要给错了[:D][:D]
 
to chenybin
thank you ,very very very very very very very very very very very veryvery very very very very very very very very very very very very very very very very very very very very very very very very very very very very very much!
 
后退
顶部