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[I]).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方法讀取屬性和其擁有的擁有並處理引用關系。