照帮助的Example做了一个例子,就是报错啊,大侠们看看吧(100分)

  • 主题发起人 主题发起人 baseyueliang
  • 开始时间 开始时间
还有一个关键是属性的定义:
property MyComp: TComponent read FMyComp write SetMyComp;
应该有一个写方法,不然还是不能流的。MyComp属性即使是未发布的,只要是public也可以的。
 
MyComp是在内部创建的,不需要写方法!不能有写方法!
 
有了写方法,还要什么流!
 
个人认为这个报错和无写方法无关,还请指教
 
我的理解:
持久化本来就是为了设计期的属性改变而用的,Public域的属性不能在设计时改变,简单类型的属性可以手工持久化只不过是Delphi持久化的一个副产品.

给你一个偏方:

unit MyEdit;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, StdCtrls, Dialogs;

type
TMyEdit = class(TEdit)
private
FMyComp: TButton;
procedure ReadMyComp(Reader: TStream);
procedure WriteMyComp(Writer: TStream);
protected
procedure DefineProperties(Filer: TFiler); override;
public
constructor Create(AOwner: TComponent); override;
//published
//不发布此属性,采用手工持久化
//安装此控件后,从组件面板上拖到窗体上,按Atl+F12就报"Stream read errror"
property MyComp: TButton read FMyComp stored true;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Standard', [TMyEdit]);
end;

{ TMyEdit }

constructor TMyEdit.Create(AOwner: TComponent);
begin
inherited;
FMyComp := TButton.Create(Self);
FMyComp.Parent:=Self;
FMyComp.Tag := 22;
FMyComp.Left := 32;
FMyComp.SetSubComponent(True);
end;

procedure TMyEdit.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineBinaryProperty('MyComp', ReadMyComp, WriteMyComp, true);
end;

procedure TMyEdit.ReadMyComp(Reader: TStream);
begin
FMyComp:=TButton(Reader.ReadComponentRes(FMyComp));
end;

procedure TMyEdit.WriteMyComp(Writer: TStream);
begin
Writer.WriteComponentRes('MyComp',FMyComp);
end;

不过你不能在dfm文件(文本格式)中看到文本数据.
 
大侠你搞错了,WriteComponentRes是TStream的吧
再说保存为二进制可以多种途径实现
 
WriteComponentRes是TStream的,我上面的代码就是使用TStream呀.

我已经说得很明确了,这是一个偏方.

保存二进制可以以多种途径实现,只不过这是其中的一种贴出来了而已.

 
不好意思,没注意看参数类型。
不过即存偏方,总有正解吧(至少宝蓝工程师知道。。。)
 
不认为有什么正解,我已经说了,执久化本来就是为published段的属性而设的,不是为Public段属性服务的.

宝兰的工程师当然知道,大不了改VCL嘛,可是.....
 
这里有宝蓝工程师的代码,不过好像没有经过调试,呵呵
----
VCL Reference
ReadBoolean, WriteBoolean, ReadComponent, WriteComponent, DefineProperties, DefineProperty, Ancestor example
This example shows how to load and save an unpublished property whose value is a component that was created at runtime. It defines two methods, LoadCompProperty and StoreCompProperty that load or save the property value. The also defines an override for the DefineProperties method where these methods are passed to the streaming system. The DefineProperties method checks the filer抯 Ancestor property to avoid saving a property value in inherited forms.

procedure TSampleComponent.LoadCompProperty(Reader: TReader);
begin
if Reader.ReadBoolean then
MyCompProperty := Reader.ReadComponent(nil);
end;

procedure TSampleComponent.StoreCompProperty(Writer: TWriter);

begin
Writer.WriteBoolean(MyCompProperty <> nil);
if MyCompProperty <> nil then
Writer.WriteComponent(MyCompProperty);
end;

procedure TSampleComponent.DefineProperties(Filer: TFiler);

function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then { check Ancestor for an inherited value }
begin
if TSampleComponent(Filer.Ancestor).MyCompProperty = nil then
Result := MyCompProperty <> nil
else if MyCompProperty = nil or
TSampleComponent(Filer.Ancestor).MyCompProperty.Name <> MyCompProperty.Name then
Result := True

else Result := False;
end
else { no inherited value -- check for default (nil) value }
Result := MyCompProperty <> nil;
end;
begin
inherited; { allow base classes to define properties }
Filer.DefineProperty('MyCompProperty', LoadCompProperty, StoreCompProperty, DoWrite);
end;
}
 
我早看过这段代码,最基本的,语法都有错误.
else if MyCompProperty = nil or
TSampleComponent(Filer.Ancestor).MyCompProperty.Name <> MyCompProperty.Name then
 
不过,TWrite公开WriteComponent这个方法总有他的道理
【简单类型的属性可以手工持久化只不过是Delphi持久化的一个副产品.】我并不认为
Delphi能让开发人员手工持久化根本原因是Delphi没有提供数组属性编辑器,之所以没有提供,是因为数组元素类型是未知的,如果是组件元素,就要使用简单类型手工持久化再配合组件类型手工持久化
 
呵呵,大家可要说Delphi提供了集合啊
可是那个东东生出来的元素都长一样的
-----
不知大家是否用过DataSet的字段编辑器,那个东东就能生出不同类型的元素
可以生出TStringField, TIntegerField, TGuidField......
 
不过TStringField, TIntegerField, TGuidField...... 这些是比较简单的对象,而且是不可视化的,因此可以采用简单类型持久化的方法对各个值域持久化
-------
但面对复杂的组件元素呢,域数量是未知的,即要求元素类型可以继承下去的
 
作为程序员可以不了解这些,但专业的控件开发人员必须得知道怎样解决这个问题
 
如果你解决不了,就必须向更有经验的控件开发人员了解,否则就退出组件开发的行列,或者就有辱优美的VCl
 
在Delphi中,只有published段的属性在设计时可控.

Public域的属性只能在运行期使用,比如一个简单类型可以手工持久化,但是没有任何实际意义的,除了加大DFM文件以外.

实际上持久化不仅仅中所谓设计期使用,运行时也可以执久化,你可以把某个控件的某一时该的状态永久的保存下来,而且在之后的个时该再装载进来.


unit MyEdit;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, StdCtrls, Dialogs;

type
TMyEdit = class(TEdit)
private
FMyComp: TEdit;
procedure LoadCompProperty(Reader: TReader);
procedure StoreCompProperty(Writer: TWriter);
protected
procedure DefineProperties(Filer: TFiler); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// published
//不发布此属性,采用手工持久化
//安装此控件后,从组件面板上拖到窗体上,按Atl+F12就报"Stream read errror"
property MyComp: TEdit read FMyComp write FMyComp;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Standard', [TMyEdit]);
end;

{ TMyEdit }

constructor TMyEdit.Create(AOwner: TComponent);
begin
inherited;
Width:=150;
Height:=150;
end;

procedure TMyEdit.LoadCompProperty(Reader: TReader);
begin
Reader.ReadListBegin;
if Reader.ReadBoolean then
FMyComp := TEdit(Reader.ReadComponent(FMyComp));
Reader.ReadListEnd;
end;

procedure TMyEdit.StoreCompProperty(Writer: TWriter);
begin
Writer.WriteListBegin;
Writer.WriteBoolean(FMyComp <> nil);
if FMyComp <> nil then
Writer.WriteComponent(FMyComp);
Writer.WriteListEnd;
end;

procedure TMyEdit.DefineProperties(Filer: TFiler);

function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then { check Ancestor for an inherited value }
begin
if TMyEdit(Filer.Ancestor).FMyComp = nil then
Result := FMyComp <> nil
else if (FMyComp = nil) or
(TMyEdit(Filer.Ancestor).FMyComp.Name <> FMyComp.Name) then
Result := True

else Result := False;
end
else { no inherited value -- check for default (nil) value }
Result := FMyComp <> nil;
end;
begin
inherited; { allow base classes to define properties }
Filer.DefineProperty('MyComp', LoadCompProperty, StoreCompProperty, DoWrite);
end;

destructor TMyEdit.Destroy;
begin
inherited;
end;

initialization
RegisterClass(TMyEdit);
RegisterClass(TComponent);
end.


以下是运行时使用的代码:


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, MyEdit, UnitComponent;

type
TForm1 = class(TForm)
ButtonSave: TButton;
ButtonLoad: TButton;
SaveDialog1: TSaveDialog;
OpenDialog1: TOpenDialog;
ListBox1: TListBox;
ButtonInit: TButton;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ButtonSaveClick(Sender: TObject);
procedure ButtonLoadClick(Sender: TObject);
procedure ButtonInitClick(Sender: TObject);
private
{ Private declarations }
public
FMyEdit:TMyEdit;
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
FMyEdit:=TMyEdit.Create(Self);
FMyEdit.Parent:=Self;
FMyEdit.Top:=200;
FMyEdit.MyComp:=Edit1;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
FMyEdit.Free;
end;

procedure TForm1.ButtonSaveClick(Sender: TObject);
var
lSaveStream: TMemoryStream;
begin
if SaveDialog1.Execute then
begin
lSaveStream := TMemoryStream.Create;
try
lSaveStream.WriteComponent(FMyEdit);
lSaveStream.Seek(0, 0);
lSaveStream.SaveToFile(SaveDialog1.FileName);
finally
lSaveStream.Free;
end;
end;
end;

procedure TForm1.ButtonLoadClick(Sender: TObject);
var
lLoadStream : TMemoryStream;
i: integer;
begin
lLoadStream := TMemoryStream.Create;
try
if OpenDialog1.Execute then
begin
lLoadStream.LoadFromFile(OpenDialog1.FileName);
lLoadStream.Seek(0, 0);
try
lLoadStream.ReadComponent(FMyEdit);
Except
end;
FMyEdit.Text:='aadfdfdfdfd';
FMyEdit.MyComp.Text:='aadfdfdfdfd';
end;
finally
lloadStream.Free;
end;
end;

procedure TForm1.ButtonInitClick(Sender: TObject);
begin
FMyEdit.Text:='aaa';
FMyEdit.MyComp.Text:='aaaaaa';
end;

end.

请注意在这个例子中,FMyComp并没有在TMyEdit.Create中创建,而是在使用的时候再赋值,所以MyComp是一个可读写属性.

这样做的原因是:
procedure TMyEdit.LoadCompProperty(Reader: TReader);
begin
Reader.ReadListBegin;
if Reader.ReadBoolean then
FMyComp := TEdit(Reader.ReadComponent(FMyComp));
Reader.ReadListEnd;
end;
这个过程中,Reader.ReadComponent(FMyComp)过程已经创建了一个TEdit类型的对象,并且它的名称与FMyComp相同,同时把它赋给了FMyComp,在TMyEdit.Create中创建,将会造成资源泻漏.

该代码可以把运行时的FMyEdit对象的所有状态都保存起来,并且把它的属性对应的Edit1的状态作为它的子对象一起保存起来.
 
仔细看看Borland的工程师的代码的说明:

"This example shows how to load and save an unpublished property whose value is a component that was created at runtime"

注意到了吗?"runtime".
 
[在Delphi中,只有published段的属性在设计时可控.

Public域的属性只能在运行期使用,比如一个简单类型可以手工持久化,但是没有任何实际意义的,除了加大DFM文件以外.

实际上持久化不仅仅中所谓设计期使用,运行时也可以执久化,你可以把某个控件的某一时该的状态永久的保存下来,而且在之后的个时该再装载进来.]
----------------------------
同意[实际上持久化不仅仅中所谓设计期使用,运行时也可以执久化],两者目的和意义不同,在设计期保存值是为了迎合开发人员,在运行期保存值却是最终用户的要求了。

 
首先感谢您的热情回复,不过这是我的想法:
1。既然是使用外部文件,没必要再覆盖虚方法DefineProperties了
2。[在Delphi中,只有published段的属性在设计时可控]:错
3。[Public域的属性只能在运行期使用]:错,就说Private域的简单变量也可以在设计期做设置(抛开设计规范),就连用纸做的书上都有说明(呵呵),至于组件变量只是还没有找到正确的方法而已
4。[但是没有任何实际意义的,除了加大DFM文件以外] 我暂时不清楚加大Form文件的好处和坏处,暂时不研究这个吧,至于实际意义:
来自:baseyueliang, 时间:2004-1-12 16:47:00, ID:2406970 | 编辑
呵呵,大家可要说Delphi提供了集合啊
可是那个东东生出来的元素都长一样的
-----
不知大家是否用过DataSet的字段编辑器,那个东东就能生出不同类型的元素
可以生出TStringField, TIntegerField, TGuidField......


来自:baseyueliang, 时间:2004-1-12 16:51:00, ID:2406989 | 编辑
不过TStringField, TIntegerField, TGuidField...... 这些是比较简单的对象,而且是不可视化的,因此可以采用简单类型持久化的方法对各个值域持久化
-------
但面对复杂的组件元素呢,域数量是未知的,即要求元素类型可以继承下去的



5。[实际上持久化不仅仅中所谓设计期使用,运行时也可以执久化,你可以把某个控件的某一时该的状态永久的保存下来,而且在之后的个时该再装载进来]. 如果我的要求不是必须设计期使用,我也就不贴这个问题寻求帮助了,我要做的东东是给开发人员用的,不是给最终用户,我不是一名专业的控件开发人员,所以来此学习与求教,
就如同DateSet的字段编辑器把各个字段类型信息值编辑到Form文件,但要比这个相对复杂(我认为)
 
后退
顶部