0

I'm a french user of Delphi 2010 so please excuse my bad english.

I've created a control from a TCustomControl. This control have a TOwnedCollection populated by TCollectionItem descendant. Those Items have a published custom list property. This list is a list of couples of integers made by me. I've written a custom designtime editor for this property and it works perfectly. So now, I want to write the list data into the dfm and it becomes a bit harder.

Here is what I've done:

TPedroGraphLineCollectionIem = class(TCollectionItem)
  published
    property PointList: TPedroIntegerCoupleList read FList write SetList
      stored GetStored;

...
procedure TPedroGraphLineCollectionIem.DefineProperties(Filer: TFiler);
begin
  inherited;
  //'PointList' : the property name
  //FList.Count > 0 : Is the list empty ?
  Filer.DefineProperty('PointList', ReadListData, WriteListData,
    (FList.Count > 0));
end;
...
procedure TPedroGraphLineCollectionIem.ReadListData(Reader: TReader);
var
  Val1, Val2: Integer;
begin
  with Reader do
  begin
    ReadListBegin;
    while not EndOfList do
    begin
      Val1 := ReadInteger;
      Val2 := ReadInteger;
      FList.AddCouple(Val1, Val2);
    end;
    ReadListEnd;
  end;
end;
...
procedure TPedroGraphLineCollectionIem.WriteListData(Writer: TWriter);
var
  I: Integer;
begin
  with Writer do
  begin
    WriteListBegin;
    for I := 0 to Count - 1 do
    begin
      WriteInteger(FList[I].Value1);
      WriteInteger(FList[I].Value2);
    end;
    WriteListEnd;
  end;
end;

The WriteListData procedure works perfectly and writes the values into the dfm. But when I try to load the Form, it always crashes and a dialog box tell me that there is an reading flow error on this property.

FList is created inside the constructor of the class.

Here is the .dfm :

object MainFrm: TMainFrm
  Left = 0
  Top = 0
  Caption = 'MainFrm'
  ClientHeight = 425
  ClientWidth = 689
  Color = clBtnFace
  ParentFont = True
  OldCreateOrder = False
  Position = poScreenCenter
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object PedroGraph1: TPedroGraph
    Left = 120
    Top = 136
    Width = 313
    Height = 209
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'Tahoma'
    TitleFont.Style = []
    Lines = <
      item
        LinePen.Color = clRed
        PointList = (
          1
          2
          3
          4)
      end>
    MarksFont.Charset = DEFAULT_CHARSET
    MarksFont.Color = clWindowText
    MarksFont.Height = -11
    MarksFont.Name = 'Tahoma'
    MarksFont.Style = []
  end
end

Error Messages (in french) :

1

Erreur lors de la lecture de TPedroGraphLineCollectionItem.PointList: Valeur de propriété incorrecte. Ignorer l'erreur et continuer ?Remarque: ceci peut provoquer la suppression de composants ou la perte de valeurs de propriété

2

Erreur lors de la lecture de TPedroGraphLineCollectionItem.□□: la propriété □□ n'existe pas. Ignorer l'erreur et continuer ?Remarque: ceci peut provoquer la suppression de composants ou la perte de valeurs de propriété

Note: the '□' char is really displayed as this.

3

Erreur lors de la lecture de TPedroGraphLineCollectionItem.□□

4

Erreur lors de la lecture de PedroGraphLines1.Lines: Valeur de propriété incorrecte. Ignorer l'erreur et continuer ?Remarque: ceci peut provoquer la suppression de composants ou la perte de valeurs de propriété

5

Erreur à la création de la fiche : Erreur de lecture du flux.

Declaration of TPedroIntegerCoupleList:

TPedroIntegerCouple = record
  Value1: Integer;
  Value2: Integer;
end;

TPedroGenericList<T> = class(TList<T>)
private
  FOnChange: TNotifyEvent;
  FUpdating: Boolean;
protected
  procedure Notify(const Item: T; Action: TCollectionNotification); override;
  procedure DoChange;
published
public
  constructor Create;
  procedure SortCustom; virtual; abstract;
  procedure Assign(const Source: TPedroGenericList<T>);
  property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;

TPedroIntegerCoupleList = class(TPedroGenericList<TPedroIntegerCouple>)
private
  function GetString: String;
  procedure SetString(const Value: String);
public
  procedure SortCustom; override;
  function AddCouple(const Value1, Value2: Integer): Integer;
  procedure InsertCouple(const Index, Value1, Value2: Integer);
  property AsString: String read GetString write SetString;
end;

Where am I wrong ?

Pedrault
  • 3
  • 3
  • Go project options, set "Use Debug DCU's" checkbox, then put breakpoints in the `TPedroGraphLineCollectionIem.ReadListData` and in the `TPedroGraphLineCollectionIem.DefineProperties` and debug the DFM loading process exactly as you debug everything else. Then come back and say which line exactly causes an error. inclusing copying verbatim text of exception (it can be just copy-pasted from the exception dialog as text) – Arioch 'The Dec 17 '13 at 10:14
  • I've tried this and nothing changed. When I'm talking about loading the form, I mean in the EDI and the breakpoints are not used. – Pedrault Dec 17 '13 at 10:23
  • you may run one IDE from another to debug the component - read about debugging DLLs and BPLs (and your component is BPL) using "host application" (which would be another copy of IDE) // also verbatim copy of exception text is anyway be a proper part of the question, and the DFM sources as well - just make them only your component and nothing more // also you do not need to open the form in IDE to compile and run and debug the program. just debug it without opening the form. – Arioch 'The Dec 17 '13 at 10:26

1 Answers1

2

I think you miss the point of DefineProperty and published They are mutually exclusive.

  • published means the VCL would store the real property by it's own means.
  • DefineProperty means there is no such real property but you would pretend like if there were some virtual one.

what is your DFM ? May it be that 'PointList' is stored twice there - as the list and as the component ?

If so - you should select only one method of saving it ether one way or another, for example making the property PUBLIC rather than PUBLISHED.

Or maybe you may try to make non-clashing names like

property PointList: TPedroIntegerCoupleList read FList write SetList stored FALSE;

Filer.DefineProperty('PointList_Virtual_DATA', ....
Arioch 'The
  • 15,799
  • 35
  • 62
  • @Pedrault and so what was in the wrong and how you fixed it ? – Arioch 'The Dec 17 '13 at 10:30
  • Yes ! It works ! I've modified the published property stored to false and I've defined a InternalPointList property and it works ! Nevertheless, I've think about what you say : the published property are automatically written. But the data were correctly written in the dfm using WriteListData... Well it works ! Thanks a lot !! – Pedrault Dec 17 '13 at 10:30
  • @Pedrault Did you personally read the DFM sources ? you did not copied the DFM here so i cannot do it for you. I can only repeat: *what is your DFM ? May it be that 'PointList' is stored twice there - as the list and as the component ?* – Arioch 'The Dec 17 '13 at 10:34
  • Here is the .dfm : InternalPointList = ( 1 2 3 4) and no, the PointList property was never stored twice ;) – Pedrault Dec 17 '13 at 10:37
  • it is PART of NEW dfm and i was talking about WHOLE OLD dfm where you maybe could see the mistake while it was not fixed yet (well, error text you also did not reported). It was a good luck that i managed to guess your problem, but next time please provide the verbatim concrete information to debug. http://sscce.org and http://www.catb.org/~esr/faqs/smart-questions.html – Arioch 'The Dec 17 '13 at 10:39
  • OK sorry, the entire part of the Item was: Lines = < item LinePen.Color = clRed PointList = ( 1 2 3 4) end> The whole dfm is a bit long and the error message were in french and it's not possible to copy the content :/ – Pedrault Dec 17 '13 at 10:43
  • I've put the WHOLE dfm in the first post. – Pedrault Dec 17 '13 at 11:05
  • hmm, instead of making SSCCE - the form wit honly the problematic component... Okay. I think the problem if when the list is tried to be loaded into `TPedroIntegerCoupleList` object before `DefineProperties` is ever called. That just guessing that `TPedroIntegerCoupleList` is derived from TList. I did not saw error text, so can only guess here. But you can always debug the VCL loading process and see how each property gets found and where it is getting routed to – Arioch 'The Dec 17 '13 at 11:17
  • Why does Delphi load this property before calling DefineProperties ? Is it because it was a published property? In this case, if I remove the DefineProperty, nothing will be written in the dfm. I think that Delphi cannot know how to save this kind of property and save nothing and then, define the property and write with WriteListData. But when Delphi tries to load it, it tries to load the list by its own way and it crashes. This for sure is the reason why it didn't work this way. But I thought that it's possible to override a write/read method even for a published property. – Pedrault Dec 17 '13 at 12:39
  • I don't know if Delphi can save any object or not, like TList derivative. But at least that `TPedroIntegerCoupleList` should have published properties to have a chance at being stored. But it does not have. // *Is it because it was a published property?* Most probably but i don't know for sure - you can debug the very process of loading DFM and see what happens after the name of the property being fetched, where and i nwhich order such a property would be searched for – Arioch 'The Dec 17 '13 at 12:51
  • you *maybe* can override for `TPedroIntegerCoupleList` class how it streams itself to/from DFM, but i ain't sure. But maybe you can. – Arioch 'The Dec 17 '13 at 12:53
  • TPedroIntegerCoupleList is a record. I hope it's the last edit I make on my first post... :P – Pedrault Dec 17 '13 at 12:57
  • Hmm, `TPedroIntegerCouple = TPair` :-) – Arioch 'The Dec 17 '13 at 13:06
  • Wow I didn't know the TPair... Thanks a lot !! – Pedrault Dec 17 '13 at 13:21
  • Generics in delphi are somewhat buggy but usable. Well, At least above D2009 :-) Read some introductionary essay - there were plenty – Arioch 'The Dec 17 '13 at 14:06
  • `TPedroIntegerCouple = array[boolean] of Integer;` - or like that :-P – Arioch 'The Dec 17 '13 at 14:08
  • Thanks a lot again ! I think it'll be more easy to use and read Item.Value1 and Item.Value2 than Item[True] or Item[False] ;) But thanks, I didn't know this kind of structure ! – Pedrault Dec 19 '13 at 19:17