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?

- 2,316
- 9
- 48
- 65
-
1Perhaps, only `OwnerDraw` property will let you center icon by yourself. – Josef Švejk Aug 31 '18 at 13:58
-
1Could 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 Answers
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.
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 ;)

- 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