How can I make a TList property from my custom control streamable?

情到浓时终转凉″ 提交于 2020-01-11 10:27:34

问题


Overview

I am writing my own listbox control derived from TCustomListBox.

I have also began implementing my own property editor to allowing editing the caption and imageindex of items at designtime (I am custom drawing the listbox and have published my own ImageList property from my control).

Problem

So far everything is working well, however I cannot get the items to stream to the dfm. I believe this could be due to the fact that the standard listbox Items property is of TStrings, and from my control I have published my own property which is also named Items, but is of TList class. I dont want the standard Items property of the listbox to appear but instead my own Items type.

So even though I can drop my control onto a form at designtime, edit the Items with my own property editor, if I run the Application the listboxes are empty, if I view the DFM at designtime and then return back to form view the items are also gone, basically they are not been stored in the DFM and I am unsure how to correct this?


Relevant extracts of the code looks like this (comments added for the purpose of this question):

My own listbox item type:

TListBoxItem = class(TObject) // Also tried TPersistent
private
  FCaption: string;
  FImageIndex: Integer;
public // Also tried Published
  property Caption: string read FCaption write FCaption;
  property ImageIndex: Integer read FImageIndex write FImageIndex;
end;

The custom control:

TMyListBox = class(TCustomListBox)
private
  FImageList: TImageList;
  FItems: TList;
  procedure SetImageList(const Value: TImageList);
protected
  procedure Notification(AComponent: TComponent;
    Operation: TOperation); override;
  procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); Override;
  procedure MeasureItem(Index: Integer; var Height: Integer); override;
public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;

  procedure AddItem(ACaption: string; AImageIndex: Integer); // adds a new item to FItems and the listbox control
published
  .....
  property Color;
  property Images: TImageList read FImageList write SetImageList;
  property Items: TList read FItems write FItems; // take over standard Items type and publish my own Items type instead
  .....
  property Sorted;
  property TabOrder;
  property TabStop;
  property Visible;
  property OnClick;
  property OnContextPopup;
  property OnDblClick;
  property OnDragDrop;
  property OnDragOver;
  //property OnDrawItem;
  property OnEndDock;
  property OnEndDrag;
  property OnEnter;
  property OnExit;
  property OnKeyDown;
  property OnKeyPress;
  property OnKeyUp;
  //property OnMeasureItem;
  property OnMouseDown;
  property OnMouseEnter;
  property OnMouseLeave;
  property OnMouseMove;
  property OnMouseUp;
  .....
end;

That is the control in its most simple form. Everything I want to do with the control such as the custom drawing and property editor for my Items property etc is working how I want.

Question

What do I need to do in order to make FItems property streamable to the DFM? The DFM should show each of my own listbox item type under Items.

Thanks.


回答1:


There are 2 possibilities:

1) Use TCollection instead of TList and your item class should be descendant of TCollectionItem. That's how panels property is implemented in TStatusBar, for example. This way is preferable if your items can be made of the same class and if they can be descendants of TCollectionItem which itself is TPersistent descendant. It looks like that:

TMyListBoxItem = class (TCollectionItem)
private
  FCaption: string;
  FImageIndex: Integer;
published  //only published properties will be saved to dfm
  property Caption: string read FCaption write FCaption;
  property ImageIndex: Integer read FImageIndex write FImageIndex;
end;

TMyListBoxItemClass = class of TMyListBoxItem; //class reference, or metaclass, we need it to properly initialize TCollection

TMyListBox = class(TCustomListBox)
private
// imageList etc...
  FItems: TCollection;
public
  constructor Create(aOwner: TComponent); override;
  destructor Destroy; override;
  // other funcs
published
  property Items: TCollection read fItems write fItems;
  //other properties
end;

//implementation
constructor TMyListBox.Create(aOwner: TComponent)
begin
  inherited;
  //other initializations
  fItems:=TCollection.Create(TMyListBoxItemClass); //so TCollection is able to create items as many as needed
end;

destructor TMyListBox.destroy;
begin
 fItems.free;
 //other destruction
 inherited Destroy;
end;

that's mostly it. By the way, never descend from TObject when you need streaming to dfm, it begins with TPersistent (which exactly means its ability to be saved and then loaded) and most fully implemented in TComponent.

2) Each item is TControl descendant which has your custom list box as its parent and form as its owner (possibly TComponent would suffice). That's how it's done in TTabControl, TPageControl, TMainMenu and other components. In that case items might contain their own items, too, all this structure is shown in 'structure' panel in IDE, which is quite handy. But in this case you can't have 'Items' property in object inspector, instead you'll have to implement your own buttons in popup menu at design time, like 'Items editor' or 'New item' etc. This implementation is useful if items can 'draw themselves', be of different types and possibly have their own children.



来源:https://stackoverflow.com/questions/37500480/how-can-i-make-a-tlist-property-from-my-custom-control-streamable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!