A
Doch, das geht. TCustomListView hat eine protected virtual -Methode namens CreateListItem() , die du in einer abgeleiteten Klasse überschreiben kannst, um ein Objekt deiner eigenen von TListItem abgeleitete Klasse zurückzugeben. Ebenso kannst du die Methode CreateListItems() überschreiben und eine von TListItems abgeleitete Klasse erstellen und zurückgeben, die eine neue Add() -Methode mit dem richtigen Rückgabewert bereitstellt.
Leider konnte Delphi noch keine Generics, als die VCL entstand. Die meisten VCL-Collections sind deshalb nur so halbwegs typsicher, weil sie mit Type Erasure implementiert sind. (Richtige Generics, z.B. in heutigem Delphi oder in .NET, haben diesen Nachteil nicht. Aber z.B. die Generics in Java verwenden intern auch Type Erasure ) Außerdem ist relativ viel Tipparbeit erforderlich, um die abgeleitete Klasse einigermaßen bequem benutzen zu können.
Unten ein Beispiel in Delphi. Du kannst es nach C++ übersetzen, ich würde es aber einfach in Delphi belassen. Idealerweise plazierst du die Klasse in einem separaten Komponenten-Package, und Delphi bietet sich an, weil es dort Class Completion gibt (du tippst nur die Klassendefinition und die IDE erzeugt auf Knopfdruck [Ctrl+Shift+C] die Funktionsprototypen), und man so einigen Code aus ComCtrls.pas mit Copy & Paste recyclen kann. (Wohlgemerkt kann auch C++Builder Delphi-Units kompilieren, das geht also auch, wenn du nicht das RAD Studio, sondern nur C++Builder hast.)
unit FormattedListView;
interface
uses
Types, ComCtrls;
type
TFormattedListItem = class;
TFormattedListItems = class;
TFormattedListItemsEnumerator = class
private
FIndex: Integer;
FListItems: TFormattedListItems;
public
constructor Create(AListItems: TFormattedListItems);
function GetCurrent: TFormattedListItem;
function MoveNext: Boolean;
property Current: TFormattedListItem read GetCurrent;
end;
TFormattedListItems = class (TListItems)
private
function DoGetItem(Index: Integer): TFormattedListItem;
procedure DoSetItem(Index: Integer; Value: TFormattedListItem);
public
function Add: TFormattedListItem;
function AddItem(Item: TFormattedListItem; Index: Integer = -1): TFormattedListItem;
function GetEnumerator: TFormattedListItemsEnumerator;
function Insert(Index: Integer): TFormattedListItem;
property Item[Index: Integer]: TFormattedListItem read DoGetItem write DoSetItem; default;
end;
TFormattedListItem = class (TListItem)
{ TODO: hier die gewünschten Eigenschaften einfügen }
end;
TCustomFormattedListView = class (TCustomListView)
protected
function CreateListItem: TListItem; override;
function CreateListItems: TListItems; override;
procedure DrawItem(Item: TListItem; Rect: TRect; State: TOwnerDrawState); override;
end;
TFormattedListView = class (TCustomFormattedListView)
published
{ TODO: hier die ganzen Properties einfügen, die in ComCtrls.TListView als published markiert sind }
end;
procedure Register;
implementation
uses
Classes, Graphics, Controls;
{ TFormattedListItems }
function TFormattedListItems.Add: TFormattedListItem;
begin
Result := inherited Add as TFormattedListItem;
end;
function TFormattedListItems.AddItem(Item: TFormattedListItem; Index: Integer): TFormattedListItem;
begin
Result := inherited AddItem (Item, Index) as TFormattedListItem;
end;
function TFormattedListItems.DoGetItem(Index: Integer): TFormattedListItem;
begin
Result := inherited Item[Index] as TFormattedListItem;
end;
procedure TFormattedListItems.DoSetItem(Index: Integer; Value: TFormattedListItem);
begin
inherited Item[Index] := Value;
end;
function TFormattedListItems.GetEnumerator: TFormattedListItemsEnumerator;
begin
Result := TFormattedListItemsEnumerator.Create (Self);
end;
function TFormattedListItems.Insert(Index: Integer): TFormattedListItem;
begin
Result := inherited Insert (Index) as TFormattedListItem;
end;
{ TFormattedListItemsEnumerator }
constructor TFormattedListItemsEnumerator.Create(AListItems: TFormattedListItems);
begin
{ Copy & Paste aus ComCtrls.TListItemsEnumerator }
inherited Create;
FIndex := -1;
FListItems := AListItems;
end;
function TFormattedListItemsEnumerator.GetCurrent: TFormattedListItem;
begin
Result := FListItems[FIndex];
end;
function TFormattedListItemsEnumerator.MoveNext: Boolean;
begin
Result := FIndex < FListItems.Count - 1;
if Result then
Inc(FIndex);
end;
{ TCustomFormattedListView }
function TCustomFormattedListView.CreateListItem: TListItem;
var
LClass: TListItemClass;
begin
{ Copy & Paste aus TCustomListView.CreateListItem() mit Substitution TListItem -> TFormattedListItem }
LClass := TFormattedListItem;
if Assigned(OnCreateItemClass) then
OnCreateItemClass(Self, LClass);
Result := LClass.Create(Items);
end;
function TCustomFormattedListView.CreateListItems: TListItems;
begin
Result := TFormattedListItems.Create(Self);
end;
procedure TCustomFormattedListView.DrawItem(Item: TListItem; Rect: TRect; State: TOwnerDrawState);
begin
TControlCanvas(Canvas).UpdateTextFlags;
if Assigned(OnDrawItem) then OnDrawItem(Self, Item, Rect, State)
else if { TODO: spezielle Darstellung erforderlich? } then
begin
{ TODO: spezielle Darstellung durchführen }
end
else
inherited; { Standarddarstellung }
end;
procedure Register;
begin
RegisterComponents ('My components', [TFormattedListView]);
end;
end.
Edit: TFormattedListView und Register() -Prozedur hinzugefügt