unit ExtraListView;

interface
uses controls,stdctrls,comctrls,classes,graphics,sysutils,windows;
type
  TValueType=(vtCaptionH1,vtCaptionH2,vtCaptionH3,vtValue); //Typ eines Eintrages
  TValueInfo=record //Ein Eintrag:
    typ:TValueType; //Typ
    name: string;   //Name
  end;
  //ausreichen großes Eintragarray (es wird natürlich immer mit Zeigern gearbeitet)
  TValueInfos=array[0..1024*1024] of TValueInfo;
  PValueInfos=^TValueInfos;
  TExtraListView=class(TObject)
  { TExtraListView macht aus einer ListBox ein ListView mit mehreren Spalten
    und unterschiedlich hohen Einträgen (bei OwnerDraw wären sie nämlich alle
    gleich hoch)
  }
  private
    fontHeight:array[0..3] of integer; //Höhe der jeweiligen Schriftgröße
    listBox:TListBox; //Zugehörige Listbox
    header:theaderControl; //Zugehöriger Spaltenkomponennte
    drawItemEvent:TDrawItemEvent; //Methode zum Ausfüllen der Spalten
    values:PValueInfos; //Einträge
    //Höhe zurück geben
    procedure ListBoxMeasureItem(Control: TWinControl; Index: Integer;  var Height: Integer);
    //Eintrag zeichnen
    procedure ListBoxDrawItem(Control: TWinControl; Index: Integer;
                              Rect: TRect; State: TOwnerDrawState);
    //Neuzeichnen wenn eine Spalte verändert wird
    procedure HeaderSectionResize(HeaderControl: THeaderControl;
                                          Section: THeaderSection);
    //Neue Eintragsliste
    procedure SetValueInfos(infos:PValueInfos);
  public
    //ListBox zurecht setzen
    constructor create(listBox:TListBox; header:theaderControl);
    destructor destroy;override;

    //Eintrag zeichnen
    procedure drawItem(i:integer);

    //Eintragsliste
    property valueInfos: PValueInfos read values write SetValueInfos;
    //Methode zum Ausfüllen der Spalten
    property OnDrawItem:TDrawItemEvent read drawItemEvent write drawItemEvent;
  end;
implementation
const fontSize:array[0..3] of integer=(18,14,10,8); //Schriftgrößen
      fontStyle:array[0..3] of TFontStyles=([fsBold],[fsBold],[fsBold],[]); //Schriftstyle
        //der jeweiligen Eintragstypen
//Höhe eines Eintrages zurückgeben
procedure TExtraListView.ListBoxMeasureItem(Control: TWinControl; Index: Integer;
  var Height: Integer);
begin
  if values=nil then exit; //keine Werte vorhanden  => abbruch
  Height:=fontheight[integer(values[index].typ)]+1;
end;

//Eintrag zeichnen
procedure TExtraListView.ListBoxDrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var tr:TRect; //Temporäres Beschränkungsrechteck
    i:integer;
begin
  if values=nil then exit;
  with ListBox.canvas do begin
    pen.Style:=psClear;
    //Zeilenhintergrund zeichnen
    if (odSelected in State)or(odFocused in State) then begin
      //Wenn markiert, nicht die ganze Zeile ausfüllen (um den gepunkteten Rand
      //nicht zu löschen)
      brush.Color:=clHighlight;
      font.Color:=clHighlightText;
      Rectangle(rect.left+1,rect.top+1,rect.right-1,rect.bottom)
    end else begin
      //Ansonsten schon
      brush.Color:=clWindow;
      font.Color:=clWindowText;
      FillRect(rect);
    end;

    //Vertikale Trennlinien
    pen.Style:=psSolid;
    if values[index].typ>=vtValue then pen.Color:=clSilver //Hellgrau bei Überschriften
    else pen.Color:=clGray; //sonst dunkelgrau
    //alle Spalten durchlaufen
    for i:=0 to Header.Sections.Count-1 do begin
      MoveTo(Header.Sections[i].Right,rect.top);
      LineTo(Header.Sections[i].Right,rect.bottom);
    end;

    //Schriftart setzen
    Font.Size:=fontsize[integer(values[index].typ)];
    Font.Style:=fontstyle[integer(values[index].typ)];
    brush.Style:=bsClear;
    if values[index].typ<vtValue then
      //Überschriften Text ausgeben
      TextRect(rect,rect.left+2,rect.top+1,values[index].name)
    else begin
      //Eintragstext ausgeben, aber auf die erste Spalte begrenzen
      tr.TopLeft:=Rect.TopLeft;
      tr.Right:=Header.Sections[0].Right;
      tr.Bottom:=rect.bottom;
      TextRect(tr,tr.left+2,tr.top+1,values[index].name);
    end;
    //Angegeben Funktion für die restlichen Spalten aufrufen
    if assigned(onDrawItem) then ondrawItem(Control,index,rect,state);
  end;
end;

//ListView neuzeichnen, wenn ein Spalte vergrößert wird
procedure TExtraListView.HeaderSectionResize(HeaderControl: THeaderControl;
  Section: THeaderSection);
begin
  ListBox.Repaint;
end;

constructor TExtraListView.create(listBox:TListBox; header:theaderControl);
var i:integer;
begin
  //Listbox und Header sind nötig
  if listBox=nil then raise exception.create('Keine ListBox übergeben (TExtraListView.create)');
  if header=nil then raise exception.create('Kein Header übergeben (TExtraListView.create)');
  self.listBox:=listBox;
  self.header:=header;
  //Eigenschaften setzten, und Ereignisse registrieren
  listBox.Style:=lbOwnerDrawVariable;
  listBox.OnMeasureItem:=ListBoxMeasureItem;
  listBox.OnDrawItem:=ListBoxDrawItem;
  header.OnSectionResize:=HeaderSectionResize;
  //Fontgrößen laden
  for i:=0 to 3 do begin
    ListBox.Canvas.Font.Size:=fontsize[i];
    ListBox.Canvas.Font.Style:=fontstyle[i];
    fontHeight[i]:=ListBox.Canvas.TextHeight('AZqgß|');
  end;
end;
destructor TExtraListView.destroy;
begin
  listBox.OnMeasureItem:=nil;
  listBox.OnDrawItem:=nil;
  inherited;
end;

//Eintrag zeichnen
procedure TExtraListView.drawItem(i:integer);
var state:TOwnerDrawState;
begin
  //Status berechnen
  state:=[];
  if ListBox.Selected[i] then Include(state,odSelected);
  if ListBox.ItemIndex=i then Include(state,odFocused);
  //zeichnen
  ListBoxDrawItem(nil,i,ListBox.ItemRect(i),state);
end;
//Eintragsliste neu speichern
procedure TExtraListView.SetValueInfos(infos:PValueInfos);
begin
  values:=infos;
end;
end.

