Mirror

Display a sort order indicator in the column header of a TListView (2) (Views: 101)


Problem/Question/Abstract:

Does anyone know how to add custom painting to the column headings in vsReport mode (short of ownerdrawing everything)? I'd like to add indication of sort order and more. I don't think the columns ImageIndex is a satisfactory solution. I would like the image of the sort indicator on the right.

Answer:

The problem is that not all versions of the listview common control support this. You have to drop to the API to make use of it. This is somewhat ackward (the common controls seem to get more cumbersome to use with each version). The listviews header line is an actual header control. A header control can display either images from an imagelist or a bitmap. Only the bitmap can be arranged to the right of the caption text. The listview offers no direct method to set a bitmap for a header, so you have to get the header controls handle and send messages to it directly. The bitmap you use should be created on form creation and destroyed on form destruction.

The following example shows the principle. There is a major snag here, though. Since the VCL listview has no idea that you changed some header properties it will happily wipe out what you did every time it feels like resetting some of the header properties, e.g. when the user resizes one of the columns. This can be dealt with if needs be, by subclassing the header control to trap the HDM_SETITEM messages that change the item properties. The TListview class already subclasses the header but the method used is private and not virtual, so not accessible.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, ImgList;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    ImageList1: TImageList;
    procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FUpArrow, FDownArrow: TBitmap;
    procedure SetColumnSortOrder(lv: TListview; Column: TListcolumn);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  commctrl;

{$R *.DFM}

procedure TForm1.SetColumnSortOrder(lv: TListview; Column: TListcolumn);
var
  hdr: HWND;
  hdritem: THDItem;
begin
  hdr := Listview_GetHeader(lv.handle);
  FillChar(hdritem, sizeof(hdritem), 0);
  hdritem.Mask := HDI_FORMAT;
  Header_GetItem(hdr, column.index, hdritem);
  hdritem.Mask := HDI_FORMAT or HDI_BITMAP;
  if column.tag = 0 then
    hdritem.hbm := FUpArrow.Handle
  else
    hdritem.hbm := FDownArrow.Handle;
  hdritem.fmt := hdritem.fmt or HDF_BITMAP_ON_RIGHT or HDF_BITMAP;
  Header_SetItem(hdr, column.index, hdritem);
end;

procedure TForm1.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
  Column.Tag := Ord(not Odd(Column.Tag));
  SetColumnSortOrder(Sender as TListview, Column);
end;

procedure TForm1.FormCreate(Sender: TObject);

  procedure MakeBitmap(var bmp: TBitmap; imageindex: Integer);
  begin
    bmp := TBitmap.Create;
    bmp.Width := imagelist1.width;
    bmp.Height := imagelist1.height;
    with bmp.Canvas do
    begin
      Brush.Color := clBtnface;
      Brush.Style := bsSolid;
      FillRect(Cliprect);
    end;
    imagelist1.Draw(bmp.canvas, 0, 0, imageindex);
  end;

begin
  MakeBitmap(FUpArrow, 1);
  MakeBitmap(FDownArrow, 0);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FUpArrow.Free;
  FDownArrow.Free;
end;

end.

<< Back to main page