TListItem erweitern



  • Hallo,

    ich möchte gerne in meinem Listview verschiedene Einträge fettdrucken. Dies soll man beim Hinzufügen des Items festlegen können, was ja erstmal als solches nicht geht. In der "OnItemDraw" Methode habe ich bisher etwas aufgesetzt, mit dem ich einen Eintrag fettdrucken kann, das Problem ist nur, dass nur bestimmte Items fett sein sollen.

    Perfekt wäre es für micht, wenn die Klasse TListItem eine Eigenschaft wie "Tag" hätte, da ich ja dann einfach 0 für normal und 1 für bold definieren könnte und anschließend einfach im OnDrawItem Event den Tag überprüfen. Leider ist diese Eigenschaft nicht vorhanden. Habe ich die Möglichkeit, diese Nachträglich hinzuzufügen, bzw eine Klasse zu schreiben, die von TListItem erbt, diese aber dann überschreibt?

    Vielen Dank für eure Antwort!



  • Nein, das geht nicht, weil du dem TListView dann auch beibringen müsstest, deine neue ListItem-Klasse zu erzeugen.
    TListItem hat allerdings ein Data Element, das du benutzen kannst.



  • 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


Anmelden zum Antworten