问题
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