2

Is it possible to fix the drawing of subitem images in a TListView so that they are not cut-off on the left hand side as shown in the image?
enter image description here

Pieter van Wyk
  • 2,316
  • 9
  • 48
  • 65
  • 1
    Perhaps, only `OwnerDraw` property will let you center icon by yourself. – Josef Švejk Aug 31 '18 at 13:58
  • 1
    Could you add an MCVE to reproduce this, please? Personally, I don't think it's possible without custom drawing, but still.. – Victoria Aug 31 '18 at 13:59
  • @Victoria I suppose your comment was addressed to me, so I must shed some light on the sense of my comment. When I recommended to use `OwnerDraw` property I *meant* to use it along with owner-draw events. In my opinion, sentence "Use OwnerDraw property" means exactly what I described previously. I believe Pieter van Wyk has enough experience to understand that ;) – Josef Švejk Aug 31 '18 at 14:17
  • 1
    @Dima, sorry, no, no not to you, I would use that @ thing :) It was to the OP, as they receive notifications about any communication within a "question thread" even without that @ thing. Well but back to the topic, personally, I don't think there's a way to horizontally (or maybe even vertically) center image in this control sub-item, as you pointed out (sorry, missed to vote for your comment, but I think it's the answer as well). – Victoria Aug 31 '18 at 14:56
  • The reason for the 1-pixel cut-off is in the gridlines. They are drawn one pixel to the right compared with the separating lines of the header columns. The pixel at which the gridlines are drawn appears to be the first pixel of a cell. The images are also drawn on the first pixel, but the gridlines are the last ones being drawn. The obvious solution, that you maybe do not want to hear, is to not draw gridlines. Perhaps you can alternatively solve it with ownerdrawing. – Tom Brunberg Aug 31 '18 at 15:17
  • @Victoria it is not a mistake, so you shouldn't apologize :) Thank you for voting. By the way - ownerdrawing allows to center image even without `OwnerDraw` property. – Josef Švejk Aug 31 '18 at 16:28
  • 1
    @Dima, no worries :) Anyway, nicely fomatted answer! (voted up, will review after :) – Victoria Aug 31 '18 at 16:36

1 Answers1

4

Well, Pieter van Wyk, I did minimal example of how you can owner-draw your TListView component in order to center images in sub-items.

Answer has been rewritten. In order to decrease size of answer I had deleted unused and wrong parts. Previous versions may be found in history of question editing.

Picture below represents work of the new code.
One orange row is a selected row. enter image description here
Images on selected row have white color around it. It is not a bug - it is a source image with such fill.

There is the code that allows to do the same thing as on the picture:

procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem; Rect: TRect;
  State: TOwnerDrawState);
var
  Bmp: TBitmap;
  Image: TBitmap;
  R: TRect;
  CenterH: Integer;
  CenterV: Integer;
  ImageIndex: Integer;
  ItemWidth: Integer;
  i: Integer;
begin
  // Set initial legth of point at the end of which image will be drawn.  
  // Column 0 is a "fixed" column
  ItemWidth := Sender.Column[0].Width;
  R := Rect;

  Bmp := TBitmap.Create;
  try
    Image := TBitmap.Create;
    try
      Bmp.SetSize(R.Width, R.Height);

      // Make fill for item
      if Item.Selected then
        Bmp.Canvas.Brush.Color := clWebOrange
      else
        Bmp.Canvas.Brush.Color := clMoneyGreen;
      Bmp.Canvas.FillRect(Bmp.Canvas.ClipRect);

      // Output image associated with 'fixed' column
      TListView(Sender).SmallImages.GetBitmap(Item.ImageIndex, Image);
      CenterH := (Sender.Column[0].Width - Image.Width) div 2;
      CenterV := (R.Height - Image.Height) div 2;
      Bmp.Canvas.Draw(CenterH, CenterV, Image);

      // Output text
      Bmp.Canvas.TextOut(CenterH + Image.Width + 6, 6, Item.Caption);

      // Draw sub-items
      for i:=0 to Item.SubItems.Count - 1 do
        begin
          // Obtain index of image
          ImageIndex := Item.SubItemImages[i];

          // Get associated image
          TListView(Sender).SmallImages.GetBitmap(ImageIndex, Image);

          // Center image
          CenterH := (Sender.Column[i+1].Width - Image.Width) div 2;
          CenterV := (R.Height - Image.Height) div 2;

          // Output image
          Bmp.Canvas.Draw(ItemWidth + CenterH, CenterV, Image);

          // Increase point where image started to be drawn
          Inc(ItemWidth, Sender.Column[i+1].Width);
        end;

      // Draw ready item's image onto sender's canvas
      Sender.Canvas.Draw(R.Left, R.Top, Bmp);
    finally
      Image.Free;
    end;
  finally
    Bmp.Free;
  end;
end;

To apply this code you must activate OwnerDraw property.

See this TListView.OwnerDraw Property that leads to docs.embarcadero. I also would like to show a quote from a link above:

Set OwnerDraw to true to allow the list view to receive the OnDrawItem event instead of the default rendering of list items.

P.S.
After column has been resized, there could be some graphical artifacts - just try to resize column in a way to hide (minimal possible size of column) and show image (any size of column that will exceeds size of associated image) and you will see what I meant.

P.S.S.
Drawing a text of sub-items I leave to you as a homework ;)

Josef Švejk
  • 1,047
  • 2
  • 12
  • 23
  • Thank you for the comprehensive answer! I have tried this and it works fine for columns with icons in them. I am now having some issues with columns that contain text to display them in a different font. Also it looks like the columns that contain text turn black when the mouse moves over them. – Pieter van Wyk Sep 03 '18 at 09:44