Search code examples
delphilistviewdrawinghoverselection

Delphi: draw selections and mouse-hover effects on ListViewDrawItem


I have a code from here: Delphi: Canvas.FillRect in List View to paint rows (I use OwnerDraw).

Therefore I need to draw selections and mouse-hover effects. And to draw a normal column resizing effect...

I have a code to move items up and down:

procedure TForm1.ListViewDragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
  DragItem, DropItem, CurrentItem, NextItem: TListItem;
begin
  if Sender = Source then
    with TListView(Sender) do
    begin
      DropItem    := GetItemAt(X, Y);
      CurrentItem := Selected;
      while CurrentItem <> nil do
      begin
        NextItem := GetNextItem(CurrentItem, SdAll, [IsSelected]);
        if DropItem = nil then DragItem := Items.Add
        else
          DragItem := Items.Insert(DropItem.Index);
        DragItem.Assign(CurrentItem);
        CurrentItem.Free;
        CurrentItem := NextItem;
      end;
    end;
end;

procedure TForm1.ListViewDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
Accept := Sender = Update_ListBox;
end;

how to draw selections and mouse-hover effects on ListViewDrawItem + column resizing effect?

Thanks!!!


Solution

  • If you want to draw selections and in other ways respond to the current state of the control, you need to write additional code for this purpose, because we are owner-drawing the control. This means that we say to Windows, "hey, don't draw anything in the client area, I'll do that". Therefore, Windows draws nothing, not even selections, focus rectangles, and mouse-hover effects.

    Fortunately, it is rather easy to implement this behaviour manually. Indeed, in the OnCustomDraw event handler, you are given a State parameter, which you can read. This is a set, and some of the possible elements include odSelected, odHotLight, and odFocused.

    Building upon our previous code, adding only a few new lines, we arrive at

    procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
      Rect: TRect; State: TOwnerDrawState);
    var
      i: Integer;
      x1, x2: integer;
      r: TRect;
      S: string;
    const
      DT_ALIGN: array[TAlignment] of integer = (DT_LEFT, DT_RIGHT, DT_CENTER);
    begin
      if SameText(Item.SubItems[1], 'done') then
      begin
          Sender.Canvas.Font.Color := clWhite;
          Sender.Canvas.Brush.Color := clGreen;
      end
      else
        if Odd(Item.Index) then
        begin
          Sender.Canvas.Font.Color := clBlack;
          Sender.Canvas.Brush.Color := $F6F6F6;
        end
        else
        begin
          Sender.Canvas.Font.Color := clBlack;
          Sender.Canvas.Brush.Color := clWhite;
        end;
      if odSelected in State then                                                    // NEW!
      begin                                                                          // NEW!
        Sender.Canvas.Font.Color := clWhite;                                         // NEW!
        Sender.Canvas.Brush.Color := clNavy;                                         // NEW!
      end;                                                                           // NEW!
      Sender.Canvas.Brush.Style := bsSolid;
      Sender.Canvas.FillRect(Rect);
      x1 := 0;
      x2 := 0;
      r := Rect;
      Sender.Canvas.Brush.Style := bsClear;
      Sender.Canvas.Draw(3, r.Top + (r.Bottom - r.Top - bm.Height) div 2, bm);
      for i := 0 to ListView1.Columns.Count - 1 do
      begin
        inc(x2, ListView1.Columns[i].Width);
        r.Left := x1;
        r.Right := x2;
        if i = 0 then
        begin
          S := Item.Caption;
          r.Left := bm.Width + 6;
        end
        else
          S := Item.SubItems[i - 1];
        DrawText(Sender.Canvas.Handle,
          S,
          length(S),
          r,
          DT_SINGLELINE or DT_ALIGN[ListView1.Columns[i].Alignment] or
            DT_VCENTER or DT_END_ELLIPSIS);
        x1 := x2;
      end;
      if odFocused in State then                                                     // NEW!
        DrawFocusRect(Sender.Canvas.Handle, Rect);                                   // NEW!
    end;
    

    Screenshot

    Notice that the 'horse' line is selected and has keyboard focus.