N.a.v. een vraag van jelmervos heb ik me vastgebeten in het creeren/copieren van componenten via Delphi's streaming mechanisme.
(jelmervos, sorry dat ik je de voorbeelden van Mark Miller nog niet gestuurd heb, maar ik kan die CD nergens vinden...
)
'k Kom een heel eind, maar stuit op wat probleempjes:
* De Owner van die componenten. TReader.ReadRootComponent zet die standaard op nil, ook al heb je de Owner van die TReader goed gezet. En Owner is een readonly property.
Ik heb 't nu "opgelost" door eerst het component in te lezen om achter z'n ComponentClass (en naam) te komen, dat component weg te gooien en opnieuw te creeren (met juiste Owner en naam), en 't component nogmaals te streamen.
Werkt, maar is niet erg netjes, en bovendien wordt de Owner van componenten die automatisch worden "meegestreamd" (ToolButtons van een ToolBar bv.) niet goed gezet.
* Alle classes die gestreamd worden, moeten expliciet via RegisterClass bekend gemaakt worden aan de ClassList, omdat anders de Reader ze niet herkent. Niet handig...
Oftewel, volgens mij doe ik iets niet goed...
Mocht iemand hier chocola van kunnen maken, dan houd ik me van harte aanbevolen!
Het voorbeeld appje bestaat uit een form met een ToolBar, 3 ToolButtons, een Memo en een Button. Die staan allemaal op Panel1.
Button1.OnClick voert CopyToForm2 uit, waarbij een nieuw Form wordt gemaakt, daar een NewPanel op gezet wordt, en vervolgens wordt de boel gestreamd.
(jelmervos, sorry dat ik je de voorbeelden van Mark Miller nog niet gestuurd heb, maar ik kan die CD nergens vinden...
'k Kom een heel eind, maar stuit op wat probleempjes:
* De Owner van die componenten. TReader.ReadRootComponent zet die standaard op nil, ook al heb je de Owner van die TReader goed gezet. En Owner is een readonly property.
Ik heb 't nu "opgelost" door eerst het component in te lezen om achter z'n ComponentClass (en naam) te komen, dat component weg te gooien en opnieuw te creeren (met juiste Owner en naam), en 't component nogmaals te streamen.
Werkt, maar is niet erg netjes, en bovendien wordt de Owner van componenten die automatisch worden "meegestreamd" (ToolButtons van een ToolBar bv.) niet goed gezet.
* Alle classes die gestreamd worden, moeten expliciet via RegisterClass bekend gemaakt worden aan de ClassList, omdat anders de Reader ze niet herkent. Niet handig...
Oftewel, volgens mij doe ik iets niet goed...
Mocht iemand hier chocola van kunnen maken, dan houd ik me van harte aanbevolen!
Het voorbeeld appje bestaat uit een form met een ToolBar, 3 ToolButtons, een Memo en een Button. Die staan allemaal op Panel1.
Button1.OnClick voert CopyToForm2 uit, waarbij een nieuw Form wordt gemaakt, daar een NewPanel op gezet wordt, en vervolgens wordt de boel gestreamd.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
| unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, ToolWin, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
ToolBar1: TToolBar;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
Button1: TButton;
Memo1: TMemo;
procedure CopyToForm2(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.CopyToForm2(Sender: TObject);
var
Stream: TMemoryStream;
Reader: TReader;
Form2: TForm;
NewPanel: TPanel;
Comp: TComponent;
CompClass: TComponentClass;
i, iPosition: integer;
sName: string;
begin
Form2 := TForm.Create(Self);
with Form2 do begin
Visible := False; // Necessary for ShowModal
Left := Self.Left + 20; // Form1 dimensions with a little offset
Top := Self.Top + 20;
Height := Self.Height;
Width := Self.Width;
NewPanel := TPanel.Create(Form2); // panel to stream to
end;
try
Stream := TMemoryStream.Create;
try
Stream.WriteComponent(Panel1); // store the panel
for i := 0 to ComponentCount-1 do begin
// stream all controls on that panel
Comp := Components[i];
if (Comp is TControl) and (TControl(Comp).Parent = Panel1) then
Stream.WriteComponent(Comp);
end;
Stream.Seek(0, soFromBeginning); // quite important... :)
Reader := TReader.Create(Stream, Stream.Size);
try
// To prevent Memo's etc. to cause a
// 'Control doesn't have a Parent' exception
Reader.Parent := NewPanel.Owner;
Reader.Owner := NewPanel.Owner; // Doesn't seem to do anything...
// First read the panel
Reader.ReadRootComponent(NewPanel);
iPosition := Reader.Position;
// get the rest of the controls
while iPosition < Stream.Size do begin
// Step 1: get the component class
Comp := Reader.ReadRootComponent(nil);
CompClass := TComponentClass(FindClass(Comp.ClassName));
sName := Comp.Name;
Comp.Free;
// Step 2: create the control with the new Owner
// and re-stream it.
Comp := CompClass.Create(NewPanel.Owner);
Comp.Name := sName;
Reader.Position := iPosition;
Comp := Reader.ReadRootComponent(nil);
// make the new Panel the parent
if Comp is TControl then
TControl(Comp).Parent := NewPanel;
iPosition := Reader.Position;
end;
finally
Reader.Free;
end;
finally
Stream.Free;
end;
Form2.ShowModal;
finally
Form2.Free;
end;
end;
initialization // so the reader wont choke on 'Class not found'...
RegisterClass(TPanel);
RegisterClass(TToolBar);
RegisterClass(TToolButton);
RegisterClass(TButton);
RegisterClass(TMemo);
finalization
// nothing to finalize
end. |