I want to make a component that includes a list of integers as one of its serialized properties. I know I can't declare a TList<integer>
as a published property, because it doesn't descend from TPersistent. I've read that you can d开发者_开发技巧efine "fake" published properties if you override DefineProperties, but I'm not quite sure how that works, especially when it comes to creating a fake property that's a list, not a single value.
Can someone point me in the right direction?
Here's a minimal example with DefineBinaryProperty
(written in D2007, no generics):
type
TTestComponent = class(TComponent)
private
FList: TList;
function GetValueCount: Integer;
function GetValues(Index: Integer): Integer;
procedure ReadValueList(Stream: TStream);
procedure SetValues(Index: Integer; Value: Integer);
procedure WriteValueList(Stream: TStream);
protected
procedure DefineProperties(Filer: TFiler); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function AddValue(Value: Integer): Integer;
procedure ClearValues;
procedure DeleteValue(Index: Integer);
property ValueCount: Integer read GetValueCount;
property Values[Index: Integer]: Integer read GetValues write SetValues;
end;
{ TTestComponent }
function TTestComponent.GetValueCount: Integer;
begin
Result := FList.Count;
end;
function TTestComponent.GetValues(Index: Integer): Integer;
begin
Result := Integer(FList[Index]);
end;
procedure TTestComponent.ReadValueList(Stream: TStream);
var
Count, I, Value: Integer;
begin
ClearValues;
Stream.Read(Count, SizeOf(Count));
for I := 0 to Count - 1 do
begin
Stream.Read(Value, SizeOf(Value));
AddValue(Value);
end;
end;
procedure TTestComponent.SetValues(Index: Integer; Value: Integer);
begin
FList[Index] := Pointer(Value);
end;
procedure TTestComponent.WriteValueList(Stream: TStream);
var
Count, I, Value: Integer;
begin
Count := ValueCount;
Stream.Write(Count, SizeOf(Count));
for I := 0 to Count - 1 do
begin
Value := Values[I];
Stream.Write(Value, SizeOf(Value));
end;
end;
procedure TTestComponent.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineBinaryProperty('ValueList', ReadValueList, WriteValueList, ValueCount > 0);
end;
constructor TTestComponent.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FList := TList.Create;
// add some values for testing
AddValue(0);
AddValue(1);
AddValue(2);
end;
destructor TTestComponent.Destroy;
begin
FList.Free;
inherited Destroy;
end;
function TTestComponent.AddValue(Value: Integer): Integer;
begin
Result := FList.Add(Pointer(Value));
end;
procedure TTestComponent.ClearValues;
begin
FList.Clear;
end;
procedure TTestComponent.DeleteValue(Index: Integer);
begin
FList.Delete(Index);
end;
and the .dfm looks like this:
object TestComponent1: TTestComponent
Left = 96
Top = 56
ValueList = {03000000000000000100000002000000}
end
The quickest, easiest method to make it happen is to use a TCollection - but you'll pay the price of "decorating" every single Integer with a TCollectionItem class! Unless there are lots of integers this is the way to go because you get Object Inspector integration for almost free (free as in little extra work hours).
If you do want to keep your list in it's current, highly efficient form (TList) then you're right, the way to go is to define your own property. Look into the Graphics.pas unit at how TPicture.DefineProperties is implemented because that's a very close match to what you need!
Idea: If you go the "DefineProperties" route you might want to look into RegisterComponentEditor because your Integer list will not be visible in Object Inspector!
精彩评论