Difference between revisions of "Streaming components/de"

From Free Pascal wiki
Jump to navigationJump to search
 
m
Line 21: Line 21:
 
These are the worker classes, which reads/writes a TComponent to/from a stream (See CreateLRSReader and CreateLRSWriter).
 
These are the worker classes, which reads/writes a TComponent to/from a stream (See CreateLRSReader and CreateLRSWriter).
 
They use a '''Driver''' to read/write a special format. At the moment there are a reader (TLRSObjectReader) and a writer (TLRSObjectWriter) for binary object format defined in the LResources unit and a writer (TXMLObjectWriter) for TDOMDocument defined in Laz_XMLStreaming.
 
They use a '''Driver''' to read/write a special format. At the moment there are a reader (TLRSObjectReader) and a writer (TLRSObjectWriter) for binary object format defined in the LResources unit and a writer (TXMLObjectWriter) for TDOMDocument defined in Laz_XMLStreaming.
The LResources unit also contains functions to convert binary format to text and back (LRSObjectBinaryToText, LRSObjectTextToBinary). The LCL prefers UTF8 for strings, while Delphi prefers Widestrings. So there are some conversion functions as well.
+
Die LResources Unit enthält auch Funktionen, um to convert binary format to text and back (LRSObjectBinaryToText, LRSObjectTextToBinary). Die LCL bevorzugt UTF8 für Strings, während Delphi Widestrings bevorzugt. Daher gibt es auch einige Konvertierungsfunktionen.
  
 
== Schreiben ihrer eigenen Komponente - Teil 1 ==
 
== Schreiben ihrer eigenen Komponente - Teil 1 ==
Line 34: Line 34:
 
   end;
 
   end;
  
== Writing a component to a stream ==
+
== Schreiben einer Komponente to a stream ==
  
 
Die Unit [[doc:lcl/lresources|LResources]] hat eine Funktion dafür:  
 
Die Unit [[doc:lcl/lresources|LResources]] hat eine Funktion dafür:  
 
   procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
 
   procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
  
It writes a component in binary format to the stream.
+
Sie schreibt eine Komponente im binären Format to the stream.
 
Zum Beispiel:
 
Zum Beispiel:
 
<pre>
 
<pre>
Line 56: Line 56:
 
</pre>
 
</pre>
  
== Reading a component from a stream ==
+
== Lesen einer Komponente from a stream ==
  
 
Die Unit LResources hat eine Fuktion dafür:  
 
Die Unit LResources hat eine Fuktion dafür:  
Line 79: Line 79:
 
== Streamable Eigenschaften ==
 
== Streamable Eigenschaften ==
  
There are some limitations, what types TReader/TWriter can stream:
+
Es gibt einige Einschränkungen, welche Typen TReader/TWriter can stream:
  
* Base types can be streamed: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. .
+
* Basistypen können be streamed: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. .
* TPersistent and descendants can be streamed
+
* TPersistent und Nachfahren can be streamed
  
* records, objects and classes not descending from TPersistent can not be streamed. To stream them you need to tell TReader/TWriter how. See below [[#Streaming custom Data - DefineProperties]].
+
* records, objects and classes not descending from TPersistent can not be streamed. To stream them you need to tell TReader/TWriter how. Siehe unten [[#Streaming custom Data - DefineProperties]].
  
 
== Streaming custom Data - DefineProperties ==
 
== Streaming custom Data - DefineProperties ==
Line 138: Line 138:
  
 
If you stream a lot of TRect, then you probably do not want to write everytime ths code.
 
If you stream a lot of TRect, then you probably do not want to write everytime ths code.
The unit LResources contains an example how to write a procedure to define a rect property:
+
Die Unit LResources enthält ein Beispiel, how to write a procedure to define a rect property:
 
   procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
 
   procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
 
    
 
    
Line 182: Line 182:
  
 
Streaming components is simple:
 
Streaming components is simple:
See the example in lazarus/examples/xmlstreaming/.
+
Siehe das Beispiel in lazarus/examples/xmlstreaming/.
  
 
== Fazit ==
 
== Fazit ==

Revision as of 16:24, 10 August 2006

Deutsch (de) English (en) français (fr) 日本語 (ja) polski (pl) português (pt)

Einleitung

Normalerweise, when you want to store data on disk or to network streams, müssen sie Code schreiben für das Laden und Speichern jeder Eigenschaft. Dieses Tutorial beschreibt, wie man Klassen schreibt, that can be loaded from and saved to streams without writing extra load/save code durch Verwendung der RTTI.

Es gibt ein Beisoiel in den Lazarus Quelltexten, welches demonstriert, wie to save a TGroupBox with a TCheckBox child to a stream and read the stream back to create a copy of both components.

 Siehe <lazaruspath>/examples/componentstreaming/

In Kombination mit RTTI Bedienelementen können sie den Umfang des Codes, der für das Verbinden der Programmdaten mit dem GUI und der Platte / dem Netzwerk benötigt wird, auf ein Minimum reduzieren.

TComponent / TPersistent

Die Klasse TPersistent ist definiert in der Unit Classes und verwendet den {$M+} Compilerschalter. Dieser Schalter veranlasst den Compiler, Laufzeit Typinformationen (engl. Run Time Type Information) (RTTI) zu erzeugen. Das bedeutet, sie und alle ihre Nachfahren erhalten einen neuen Klassenabschnitt published. 'Published' Eigenschaften sind sichtbar als 'public', aber zusätzlich ist ihre Struktur zur Laufzeit erreichbar. Das bedeutet, alle published Eigenschaften können zur Laufzeit gelesen und geschrieben werden. Die IDE zum Beispiel verwendet dies, um mit Komponenten zu arbeiten, die sie nicht kennt.

TComponent erweitert TPersistent durch die Möglichkeit, child components zu haben. Das ist wichtig für streaming, where one component is the root component also called lookup root with a list of child components.

TReader / TWriter

These are the worker classes, which reads/writes a TComponent to/from a stream (See CreateLRSReader and CreateLRSWriter). They use a Driver to read/write a special format. At the moment there are a reader (TLRSObjectReader) and a writer (TLRSObjectWriter) for binary object format defined in the LResources unit and a writer (TXMLObjectWriter) for TDOMDocument defined in Laz_XMLStreaming. Die LResources Unit enthält auch Funktionen, um to convert binary format to text and back (LRSObjectBinaryToText, LRSObjectTextToBinary). Die LCL bevorzugt UTF8 für Strings, während Delphi Widestrings bevorzugt. Daher gibt es auch einige Konvertierungsfunktionen.

Schreiben ihrer eigenen Komponente - Teil 1

Eine maßgeschneiderte Komponente kann so einfach sein wie: type

 TMyComponent = class(TComponent)
 private
   FID: integer;
 published
   property ID: integer read FID write FID;
 end;

Schreiben einer Komponente to a stream

Die Unit LResources hat eine Funktion dafür:

 procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);

Sie schreibt eine Komponente im binären Format to the stream. Zum Beispiel:

procedure TForm1.Button1Click(Sender: TObject);
var
  AStream: TMemoryStream;
begin
  AStream:=TMemoryStream.Create;
  try
    WriteComponentAsBinaryToStream(AStream,AGroupBox);
    ... save stream somewhere ...
  finally
    AStream.Free;
  end;
end;

Lesen einer Komponente from a stream

Die Unit LResources hat eine Fuktion dafür:

 procedure ReadComponentFromBinaryStream(AStream: TStream;
   var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
  • AStream is the stream containing a component in binary format.
  • RootComponent is either an existing component, which data will be overwritten, or it is nil and a new component will be created.
  • OnFindComponentClass is a function, that is used by TReader to get the class from the classnames in the stream. For example:
procedure TCompStreamDemoForm.OnFindClass(Reader: TReader;
  const AClassName: string; var ComponentClass: TComponentClass);
begin
  if CompareText(AClassName,'TGroupBox')=0 then
    ComponentClass:=TGroupBox
  else if CompareText(AClassName,'TCheckBox')=0 then
    ComponentClass:=TCheckBox;
end;
  • TheOwner is the component owner, when creating a new component.

Streamable Eigenschaften

Es gibt einige Einschränkungen, welche Typen TReader/TWriter can stream:

  • Basistypen können be streamed: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. .
  • TPersistent und Nachfahren can be streamed

Streaming custom Data - DefineProperties

You can stream additinal arbitrary data by overriding DefineProperties. This allows to stream all data, that have no base types. For example to stream a variable FMyRect: TRect of your component, add the following three methods to your component:

procedure DefineProperties(Filer: TFiler); override;
procedure ReadMyRect(Reader: TReader);
procedure WriteMyRect(Writer: TWriter);

Mit dem folgenden Code:

procedure TMyComponent.DefineProperties(Filer: TFiler);
var
  MyRectMustBeSaved: Boolean;
begin
  inherited DefineProperties(Filer);
  MyRectMustBeSaved:=(MyRect.Left<>0)
                     or (MyRect.Top<>0)
                     or (MyRect.Right<>0)
                     or (MyRect.Bottom<>0);
  Filer.DefineProperty('MyRect',@ReadMyRect,@WriteMyRect,MyRectMustBeSaved);
end;

procedure TMyComponent.ReadMyRect(Reader: TReader);
begin
  with Reader do begin
    ReadListBegin;
    FMyRect.Left:=ReadInteger;
    FMyRect.Top:=ReadInteger;
    FMyRect.Right:=ReadInteger;
    FMyRect.Bottom:=ReadInteger;
    ReadListEnd;
  end;
end;

procedure TMyComponent.WriteMyRect(Writer: TWriter);
begin
  with Writer do begin
    WriteListBegin;
    WriteInteger(FMyRect.Left);
    WriteInteger(FMyRect.Top);
    WriteInteger(FMyRect.Right);
    WriteInteger(FMyRect.Bottom);
    WriteListEnd;
  end;
end;

This will save MyRect as a property 'MyRect'.

If you stream a lot of TRect, then you probably do not want to write everytime ths code. Die Unit LResources enthält ein Beispiel, how to write a procedure to define a rect property:

 procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
 

This way the above code can be written this short:

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  DefineRectProperty(Filer,'MyRect',@FMyRect,nil);
end;

Schreiben ihrer eigenen Komponente - Teil 2

Jetzt kann das Beispiel erweitert werden und wir können beliebige Eigenschaften verwenden mit nur ein paar Zeilen Code:

type
  TMyComponent = class(TComponent)
  private
    FID: integer;
    FRect1: TRect;
    FRect2: TRect;
  protected
    procedure DefineProperties(Filer: TFiler); override;
  public
    property Rect1: TRect read FRect1 write FRect1;
    property Rect2: TRect read FRect2 write FRect2;
  published
    property ID: integer read FID write FID;
  end;

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  DefineRectProperty(Filer,'Rect1',@FRect1,nil);
  DefineRectProperty(Filer,'Rect2',@FRect2,nil);
end;

Diese Komponente kann nun gespeichert, geladen oder von den RTTI Bedienelementen verwendet werden. Sie müssen keinen weiteren Code schreiben.

Writing and Reading components from/to XML

Streaming components is simple: Siehe das Beispiel in lazarus/examples/xmlstreaming/.

Fazit

RTTI ist ein leistungsstarker Mechanismus, der verwendet werden kann to easily stream whole classes and helps avoiding writing a lot of boring load/save code.

Siehe auch

RTTI controls