首页  编辑  

实现TCollection类

Tags: /超级猛料/VCL/VCL.非可视组件和类/   Date Created:

实现TCollection类

TCollection非常有用,可以在控件中实现列表,并保存数据。

标  题: Implementing TCollection

发信站: BBS 水木清华站 (Tue Sep 22 18:22:02 1998)

 This document is intended for those needing to descend from a class

that manages an array of lightweight persistent objects of the same

type.  The class that best accomplishes this is TCollection and

TCollectionItem.  For example, TCollection is used to manage Panels

in a TStatusBar, Columns in a TDBGrid, or Constraints in a TTable.

 

 This document begins with a discussion of the expected behavior of  

TCollection descendants, followed by a listing of the minimal steps

necessary to implement a TCollection descendant, a listing of

the component source, and finally some notes on design decisions

and ideas for expansion of your TCollection descendant component.

 

General Discussion

------------------

 To become familiar with the default behavior of TCollection, try

adding a TStatusBar component to a form, click the ellipses of the

Panels property, press the Add button of the "Editing Panels".

This last step adds a TStatusPanel to the editor.  Click on

on the TStatusPanel item in the editor and notice the change in

the object inspector.  Instead of seeing TStatusBar now you will

see StatusBar1.Panels[0] reflected in the Object Inspector.  

 

 There are three major players involved with collections.  A

collection item (TCollectionItem) descendant, a TCollection that

manages the list of TCollectionItems, and a component that contains

the TCollection as one of it's properties.  In our above example of  

TStatusBar, TStatusBar contains a descendant of TCollection called

TPanels and TPanels manages a list of TCollectionItem descendants

called TPanel.  Notice that each TCollectionItem contains one or

more properties; for instance, TPanels contains Alignment, Bevel,  

Style, Text, and Width properties.  This list changes depending on

the definition of your TCollectionItem descendant.

Creating a Minimal TCollection Implementation

---------------------------------------------

In a new unit you must first define three new descendant classes

from TCollectionItem, TCollection and a TComponent.

 TMyCollectionItem = class(TCollectionItem)

 TMyCollection = class(TCollection)

 TMyComponent = class(TComponent)

 To make TMyCollectionItem functional, you need to define

one or more properties to contain information to be tracked

by the collection mechanism.  The example defines a Text and

a MoreStuff integer property.  You will also need to override

the GetDisplayName method to supply the string shown for each

item in the collection property editor:

TMyCollectionItem = class(TCollectionItem)

 private

   FText: string;

   FMoreStuff: LongInt;

   function GetDisplayName: string; override;

   procedure SetText(const Value: string);

   procedure SetMoreStuff(const Value: LongInt);

 published

   property Text: string read FText write SetText;

   property MoreStuff: LongInt  

     read FMoreStuff write SetMoreStuff;

 end;

 Next, define the TCollection descendant.  This class will  

keep track of the component the collection belongs to,  

override the GetOwner method to accomodate streaming, and  

manage an array of the previously defined TCollectionItem

descendants.  

 You will need to define a new static constructor.  The parameter

passed in this constructor is the reference to the component  

that contains the collection.  Also in the constructor you  

need to populate the ItemClass property with the class of your

TCollection item descendant. Note: ItemClass returns the class  

(descended from TCollectionItem) to which the items in the  

collection belong.  

TMyCollection = class(TCollection)

 private

   FMyComponent: TMyComponent;

   function GetItem(Index: Integer): TMyCollectionItem;

   procedure SetItem(Index: Integer; Value: TMyCollectionItem);

 protected

   function GetOwner: TPersistent; override;

 public

   constructor Create(MyComponent: TMyComponent);

   function Add: TMyCollectionItem;

   property Items[Index: Integer]: TMyCollectionItem  

     read GetItem write SetItem; default;

 end;

 Finally, define the component that will contain the collection.

The component will contain a property descended from the  

TCollection type defined previously.  The TCollection property

will need a private field, an access method to the private field,

and storage allocated in the constructor and freed in the

destructor.  

Note: See The Developers Guide for more information on creating  

custom components.

 TMyComponent = class(TComponent)

 private

   FItems: TMyCollection;

   procedure SetItems(Value: TMyCollection);

 public

   constructor Create(AOwner: TComponent); override;

   destructor Destroy; override;

 published

   property Items: TMyCollection  

     read FItems write SetItems;

 end;

Complete Unit Listing

---------------------

unit Collec1;

interface

//  Note: TCollection and TCollectionItem are defined in Classes.Pas.

uses Classes;

type

 TMyComponent = class;

 TMyCollectionItem = class(TCollectionItem)

 private

   FText: string;

   FMoreStuff: LongInt;

   function GetDisplayName: string; override;

   procedure SetText(const Value: string);

   procedure SetMoreStuff(const Value: LongInt);

 public

 published

   property Text: string read FText write SetText;

   property MoreStuff: LongInt read FMoreStuff write SetMoreStuff;

 end;

 TMyCollection = class(TCollection)

 private

   FMyComponent: TMyComponent;

   function GetItem(Index: Integer): TMyCollectionItem;

   procedure SetItem(Index: Integer; Value: TMyCollectionItem);

 protected

   function GetOwner: TPersistent; override;

 public

   constructor Create(MyComponent: TMyComponent);

   function Add: TMyCollectionItem;

   property Items[Index: Integer]: TMyCollectionItem  

     read GetItem write SetItem; default;

 end;

 TMyComponent = class(TComponent)

 private

   FItems: TMyCollection;

   procedure SetItems(Value: TMyCollection);

 public

   constructor Create(AOwner: TComponent); override;

   destructor Destroy; override;

 published

   property Items: TMyCollection read FItems write SetItems;

 end;

procedure Register;

implementation

procedure Register;

begin

 RegisterComponents('Sample', [TMyComponent]);

end;

{ TMyCollectionItem }

// Note: Inherited default behavior of GetDisplayName is to  

// return the classname.

function TMyCollectionItem.GetDisplayName: string;

begin

 Result := Text;

 if Result = '' then Result := inherited GetDisplayName;

end;

procedure TMyCollectionItem.SetText(const Value: string);

begin

 if FText <> Value then

   FText := Value;

end;

procedure TMyCollectionItem.SetMoreStuff(const Value: LongInt);

begin

 if FMoreStuff <> Value then

   FMoreStuff:= Value;

end;

{ TMyCollection }

constructor TMyCollection.Create(MyComponent: TMyComponent);

begin

 inherited Create(TMyCollectionItem);

 FMyComponent := MyComponent;

end;

function TMyCollection.Add: TMyCollectionItem;

begin

 Result := TMyCollectionItem(inherited Add);

end;

function TMyCollection.GetItem(Index: Integer): TMyCollectionItem;

begin

 Result := TMyCollectionItem(inherited GetItem(Index));

end;

procedure TMyCollection.SetItem(Index: Integer;  

       Value: TMyCollectionItem);

begin

 inherited SetItem(Index, Value);

end;

// Note: You must override GetOwner in Delphi 3.x to get

// correct streaming behavior.

function TMyCollection.GetOwner: TPersistent;

begin

 Result := FMyComponent;

end;

{ TMyComponent }

constructor TMyComponent.Create(AOwner: TComponent);

begin

 inherited Create(AOwner);

 FItems := TMyCollection.Create(Self);

end;

destructor TMyComponent.Destroy;

begin

 FItems.Free;

 inherited Destroy;

end;

procedure TMyComponent.SetItems(Value: TMyCollection);

begin

 FItems.Assign(Value);

end;

end.

{--------------------------------------------------------------------}

Notes

-----

 In this minimal example we didn't override the Assign method for

the TCollectionItem, but this method should have further support.

Here's an example of how you might implement Assign in the above  

project:

procedure TMyCollectionItem.Assign(Source: TPersistent);

begin

 if Source is TMyCollectionItem then

 begin

   Text := TMyCollectionItem(Source).Text;

   MoreStuff := TMyCollectionItem(Source).MoreStuff;

   Exit;

 end;

 inherited Assign(Source);

end;

 Also not included in the above project is the logic needed to  

notify the TCollection class when one of it's contained items has

changed.  This could be particularly important in a visual control

such as TStatusBar.  TCollection supplies a virtual Update method

for handling this behavior.  See TStatusBar or THeaderControl

in \source\vcl\commctrls.pas for further examples.