Streaming components/pt
│
Deutsch (de) │
English (en) │
français (fr) │
日本語 (ja) │
polski (pl) │
português (pt) │
Introdução
Normalmente, quando você necessita armazenar dados em disco ou rede, você deve escrever código para carregamento e o salvamento de cada propriedade. Este tutorial descreve como escrever classes, estas podem ter carregado de e conservado para os streams(fluxos) sem código extra da escrita ler/salvar usando o RTTI.
Aqui tem um exemplo nos fontes do lazarus, demonstrando como salvar um TGroupBox com um TCheckBox filho para uma stream(fluxo) e ler uma stream de volta para criar uma cópia de ambos os componentes.
Veja <lazaruspath>/examples/componentstreaming/
Na combinação com RTTI controls você pode reduzir o tamanho de código necessário para conectar os dados do programa com a GUI e o Disco/Rede para um minímo.
TComponent / TPersistent
A classe TPersistent é definida na unidade Classes e estão utilizando a diretiva de compilação {$M+}. Esta diretiva diz ao compilado para criar o Tipo de Informação em Tempo de Execução (RTTI). Isto significa que e todos seus descendentes começam uma nova seção published da classe . Propriedades da 'Published'(difundida) são visíveis como a 'public'(pública), mas adicionalmente sua estrutura é acessível em tempo de execução. O que significa que toda propriedade difundida pode ser lida e escrita em tempo de execução. A IDE por exemplo usa este para trabalhar com componentes que nunca se ouviu.
TComponent extende a habilidade de TPersistent para ter componentes filhos. Isto é importante para streaming, onde um componente é o root component(raiz) também chamado de lookup root com uma lista de componentes filho.
TReader / TWriter
Aqui estão as classes que iremos trabalhar, que le/escreve um componente para/de um stream (Veja CreateLRSReader e CreateLRSWriter). Eles usam um Driver para ler/escrever um especial formato. No momento aqui temos um leitor (TLRSObjectReader) e um escritor (TLRSObjectWriter) de formato binário do objeto definido na unidade LResources e um escritor (TXMLObjectWriter) para TDOMDocument definido no Laz_XMLStreaming. A unidade LResources também contém funções para converter do formato binário par textual e vice-versa (LRSObjectBinaryToText, LRSObjectTextToBinary). A LCL prefere UTF8 para strings, enquanto o Delphi prefere Widestrings. Assim há algumas funções da conversão também.
Escrevendo seu próprio componente - Parte 1
Um componente próprio pode ser um simples como: type
TMyComponent = class(TComponent) private FID: integer; published property ID: integer read FID write FID; end;
Escrevendo um componente para uma stream
A unidade LResources tem a seguinte função:
procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
Ela escreve um componente no formato binário num stream. Por exemplo:
procedure TForm1.Button1Click(Sender: TObject); var AStream: TMemoryStream; begin AStream:=TMemoryStream.Create; try WriteComponentAsBinaryToStream(AStream,AGroupBox); ... salvar a stream em algum lugar ... finally AStream.Free; end; end;
Lendo um componente de um stream
A unidade LResources tem a seguinte função:
procedure ReadComponentFromBinaryStream(AStream: TStream; var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
- AStream é a stream contendo um componente no formato binário.
- RootComponent é qualquer componente existente, cujo os dados serão sobre-escritos, ou esse é nil(valor vazio) um componente novo será criado.
- OnFindComponentClass é uma função, que é usada pelo TReader para pegar a classe a partir de classnames na stream. Por exemplo:
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 é o componente raiz ou dono, quando criando um novo componente.
Propriedades que podem ser guardadas num Stream
Há algumas limitações, que tipos TReader/TWriter podem colocar no stream:
- Tipos básicos podem ser armazenados na stream: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. .
- TPersistent e seus descendentes podem ser armazenados na stream
- records, objects e classes não descendendo de TPersistent não podem ser guardadas. Para armazená-los você precisa dizer como no TReader/TWriter. Veja a seguir em armazenando dados customizados - DefineProperties.
Armazenadno Dados Customizados - DefineProperties
Você pode guardar arbitrariamente dados sobrescrevendo(overriding) DefineProperties. Isto habilita a guardar qualquer dado na stream, que não tem tipos básico. Por exemplo para armazenar uma variável FMyRect: TRect' de seu componente, adicione os seguintes três métodos:
procedure DefineProperties(Filer: TFiler); override; procedure ReadMyRect(Reader: TReader); procedure WriteMyRect(Writer: TWriter);
Com o seguinte código:
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;
Este irá salvar MyRect como uma propriedade 'MyRec'.
Se você tem muitos objetos TRect, então você provavelmente não precisa escrever este código toda vez. A unidade LResources contém um exemplo de como escrever um procedimento para definir uma propriedade rect:
procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
Desta maneira o código acima pode ser escrito de forma curta:
procedure TMyComponent.DefineProperties(Filer: TFiler); begin inherited DefineProperties(Filer); DefineRectProperty(Filer,'MyRect',@FMyRect,nil); end;
Escrevendo seu próprio componente - Parte 2
Agora o exemplo pode ser extendido e nós podemos usar propriedades arbitrárias com somente algumas linhas de código:
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;
Este componente pode agora ser salvo, lido ou usado pelos RTTI controls. Você não precisa escrever algum código a mais.