Delphi: bug when resizing columns of List View (on DrawItem) - delphi

Enable Autosize of columns and enable OwnerDraw for a List View. Then add a code below from HERE:
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;
Actively resize the penultimate column if it has autosize. It bill be bugs:
How to prevent these bugs?­­­­­­­­
Thanks!

The bug is in the TListColumn.GetWidthin 'comctrls.pas'. The VCL is retrieving a wrong column width while resizing columns when 'AutoSize' on the columns is set, hence you're drawing item text all over the columns.
I looked at the VCL code for a few minutes and couldn't figure out what's wrong, but setting the value in a getter is suspicious enough.
Anyway, for a workaround, instead of
inc(x2, ListView1.Columns[i].Width);
use this:
inc(x2, ListView_GetColumnWidth(ListView1.Handle, ListView1.Columns[i].Index));

Related

How can I restore the Highlight function in an owner-drawn ListView

I wrote some code to make the first row white, second grey, third white, and so on. To do that, I had to use OwnerDraw=true, but now the ListView doesn't respond as it used to when you hover over a row. How do I add that back?
This is what I have now:
procedure TAchievementTracker.lvAchievementsDrawItem(Sender: TSMView;
Item: TSMListItem; 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 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;
Sender.Canvas.Brush.Style := bsSolid;
Sender.Canvas.FillRect(Rect);
x1 := 0;
x2 := 0;
r := Rect;
Sender.Canvas.Brush.Style := bsClear;
for i := 0 to lvAchievements.Columns.Count - 1 do
begin
inc(x2, lvAchievements.Columns[i].Width);
r.Left := x1;
r.Right := x2;
if i = 0 then
S := Item.Caption
else
S := ' ' + Item.SubItems[i-1];
DrawText(Sender.Canvas.Handle,
S,
length(S),
r,
DT_SINGLELINE or DT_ALIGN[lvAchievements.Columns[i].Alignment] or
DT_VCENTER or DT_END_ELLIPSIS);
x1 := x2;
end;
end;
There's a simpler way to colour the lines of a list view control than to use full owner drawing. You can use the OnCustomDrawItem event even if OwnerDraw is False:
procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
const
BgColors: array[Boolean] of TColor = (clWhite, clSilver);
FgColors: array[Boolean] of TColor = (clBlack, clBlack);
begin
Sender.Canvas.Brush.Color := BgColors[Odd(Item.Index)];
Sender.Canvas.Font.Color := FgColors[Odd(Item.Index)];
end;
This actually preserves the themed hover and selected effects:
The problem is that the standard themed effects typically look bad together with the custom colours.
So perhaps it is better to fully custom-draw it (OwnerDraw = True):
procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
const
BgColors: array[Boolean] of TColor = (clWhite, clSilver);
FgColors: array[Boolean] of TColor = (clBlack, clBlack);
Alignments: array[TAlignment] of TTextFormats = (tfLeft, tfRight, tfCenter);
var
LV: TListView;
i, x1, x2: Integer;
R: TRect;
S: string;
begin
LV := Sender as TListView;
if [odSelected, odHotLight] * State <> [] then
begin
LV.Canvas.Brush.Color := clNavy;
LV.Canvas.Font.Color := clWhite;
end
else
begin
LV.Canvas.Brush.Color := BgColors[Odd(Item.Index)];
LV.Canvas.Font.Color := FgColors[Odd(Item.Index)];
end;
LV.Canvas.Brush.Style := bsSolid;
LV.Canvas.FillRect(Rect);
x1 := 0;
x2 := 0;
R := Rect;
LV.Canvas.Brush.Style := bsClear;
for i := 0 to LV.Columns.Count - 1 do
begin
Inc(x2, LV.Columns[i].Width);
R.Left := x1;
R.Right := x2;
if i = 0 then
S := Item.Caption
else
S := Item.SubItems[i - 1];
S := #32 + S;
LV.Canvas.TextRect(R, S, [tfSingleLine,
Alignments[LV.Columns[i].Alignment], tfVerticalCenter, tfEndEllipsis]);
x1 := x2;
end;
if odFocused in State then
begin
LV.Canvas.Brush.Style := bsSolid;
LV.Canvas.Brush.Color := clBlack;
LV.Canvas.Font.Color := clWhite;
Rect.Inflate(-1, -1);
DrawFocusRect(LV.Canvas.Handle, Rect);
end;
end;
Unfortunately, as you can see, this introduces new problems, such as an alignment issue, which I "solved" in a very sloppy way in this snippet. Also, it seems like this approach doesn't allow you to produce a hover ("hot") effect. The snippet above supports highlight and focus, but not hover.
OK, let's do it!
If you really, really, want the hot effect, there's always a way:
Set the list view control's Tag to -1, let
procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
const
BgColors: array[Boolean] of TColor = (clWhite, clSilver);
FgColors: array[Boolean] of TColor = (clBlack, clBlack);
Alignments: array[TAlignment] of TTextFormats = (tfLeft, tfRight, tfCenter);
var
LV: TListView;
i, x1, x2: Integer;
R: TRect;
S: string;
begin
LV := Sender as TListView;
if ListView1.Tag = Item.Index then //
begin //
LV.Canvas.Brush.Color := clSkyBlue; // NEW
LV.Canvas.Font.Color := clBlack; //
end //
else if odSelected in State then
begin
LV.Canvas.Brush.Color := clNavy;
LV.Canvas.Font.Color := clWhite;
end
else
begin
LV.Canvas.Brush.Color := BgColors[Odd(Item.Index)];
LV.Canvas.Font.Color := FgColors[Odd(Item.Index)];
end;
LV.Canvas.Brush.Style := bsSolid;
LV.Canvas.FillRect(Rect);
x1 := 0;
x2 := 0;
R := Rect;
LV.Canvas.Brush.Style := bsClear;
for i := 0 to LV.Columns.Count - 1 do
begin
Inc(x2, LV.Columns[i].Width);
R.Left := x1;
R.Right := x2;
if i = 0 then
S := Item.Caption
else
S := Item.SubItems[i - 1];
S := #32 + S;
LV.Canvas.TextRect(R, S, [tfSingleLine,
Alignments[LV.Columns[i].Alignment], tfVerticalCenter, tfEndEllipsis]);
x1 := x2;
end;
if (odFocused in State) and not (odNoFocusRect in State) then
begin
LV.Canvas.Brush.Style := bsSolid;
LV.Canvas.Brush.Color := clBlack;
LV.Canvas.Font.Color := clWhite;
Rect.Inflate(-1, -1);
DrawFocusRect(LV.Canvas.Handle, Rect);
end;
end;
and add the following OnMouseMove handler:
procedure TForm1.ListView1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
LI: TListItem;
Idx: Integer;
begin
LI := ListView1.GetItemAt(X, Y);
if Assigned(LI) then
Idx := LI.Index
else
Idx := -1;
if Idx <> ListView1.Tag then
begin
ListView1.Tag := Idx;
ListView1.Invalidate; // maybe overkill
end;
end;
and the following OnMouseLeave handler:
procedure TForm1.ListView1MouseLeave(Sender: TObject);
begin
if ListView1.Tag <> -1 then
begin
ListView1.Tag := -1;
ListView1.Invalidate;
end;
end;

Add graphical bar to a StringGrid col

Using Delphi 2010 and a TStringGrid component, I currently display five filds from a database query.
Here is a simplied example of what i am doing
//set up the grid
procedure TGriddata.FormCreate(Sender: TObject);
begin
grdMain.Rows[0].commatext:='"One","Two","Three","Four","Five"';
grdMain.ColWidths[0]:= 50;
grdMain.ColWidths[1]:= 175;
grdMain.ColWidths[2]:= 175;
grdMain.ColWidths[3]:= 100;
grdMain.ColWidths[4]:= 300;
end;
//display the data in the grid
//note, I am not showing my creation, execution, or destroy of the query
procedure TGriddata.load;
begin
...
grdMain.Cells[0,row]:= FieldByName('one').AsString;
grdMain.Cells[1,row]:= FieldByName('two').AsString;
grdMain.Cells[2,row]:= FieldByName('three').AsString;
grdMain.Cells[3,row]:= FieldByName('four').AsString;
//draw progress bar here
...
end;
One of the columns ("Five") needs to display a navy blue horizontal bar in the col. It should also diplay some text centered in the bar. I have no expereince using the custom drawing. What properties do i set to only custom draw the one column and use the default drawing for the other columns?
Add the text to the cells like you normally would. But you have to draw those bars in the OnDrawCell event. Leave DefaultDrawing as is (True by default), and erase the already drawn cell text in those columns by filling it in advance:
procedure TForm1.grdMainDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
Progress: Single;
R: TRect;
Txt: String;
begin
with TStringGrid(Sender) do
if (ACol = 4) and (ARow >= FixedRows) then
begin
Progress := StrToFloatDef(Cells[ACol, ARow], 0) / 100;
Canvas.FillRect(Rect);
R := Rect;
R.Right := R.Left + Trunc((R.Right - R.Left) * Progress);
Canvas.Brush.Color := clNavy;
Canvas.Rectangle(R);
Txt := Cells[ACol, ARow] + '%';
Canvas.Brush.Style := bsClear;
IntersectClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
Canvas.Font.Color := clHighlightText;
DrawText(Canvas.Handle, PChar(Txt), -1, Rect, DT_SINGLELINE or
DT_CENTER or DT_VCENTER or DT_END_ELLIPSIS or DT_NOPREFIX);
SelectClipRgn(Canvas.Handle, 0);
ExcludeClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
Canvas.Font.Color := clWindowText;
DrawText(Canvas.Handle, PChar(Txt), -1, Rect, DT_SINGLELINE or
DT_CENTER or DT_VCENTER or DT_END_ELLIPSIS or DT_NOPREFIX);
SelectClipRgn(Canvas.Handle, 0);
end;
end;
For more options, you might consider this DrawStatus routine.
Here you can view a sample (Draw percentage in a cell in a Grid), to draw a bar in a cell of a TStringGrid.
The explanation is in spanish, but you can download the code, that is very simple.
Also you can use authomatic translation on right of page.
procedure TFormDrawCell.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
const
STR_EMPTY = '';
CHAR_PERCENT = '%';
SPACE_TO_CENTER_CELLTEXT = 0;
var
fValue: Integer;
ActualPenColor, ActualBrushColor: TColor;
EmptyDS: Boolean;
DrawRect: TRect;
fWidth1, fLeft2: Integer;
StrValue: string;
begin
if not (Column.FieldName = 'Precent') then
Exit;
if not (cbdraw.Checked) then
Exit;
EmptyDS := ((TDBGrid(Sender).DataSource.DataSet.EoF) and
(TDBGrid(Sender).DataSource.DataSet.Bof));
if (Column.Field.IsNull) then begin
fValue := -1;
StrValue := STR_EMPTY;
end
else begin
fValue := Column.Field.AsInteger;
StrValue := IntToStr(fValue) + CHAR_PERCENT;
end;
DrawRect := Rect;
InflateRect(DrawRect, -1, -1);
fWidth1 := (((DrawRect.Right - DrawRect.Left) * fValue) DIV 100);
ActualPenColor := TDBGrid(Sender).Canvas.Pen.Color;
ActualBrushColor := TDBGrid(Sender).Canvas.Brush.Color;
TDBGrid(Sender).Canvas.Pen.Color := clHighlight;
TDBGrid(Sender).Canvas.Brush.Color := clWhite;
TDBGrid(Sender).Canvas.Rectangle(DrawRect);
if (fValue > 0) then begin
TDBGrid(Sender).Canvas.Pen.Color := clSkyBlue;
TDBGrid(Sender).Canvas.Brush.Color := clSkyBlue;
DrawRect.Right := DrawRect.Left + fWidth1;
InflateRect(DrawRect, -1, -1);
TDBGrid(Sender).Canvas.Rectangle(DrawRect);
end;
if not (EmptyDS) then begin
DrawRect := Rect;
InflateRect(DrawRect, -2, -2);
TDBGrid(Sender).Canvas.Brush.Style := bsClear;
fLeft2 := DrawRect.Left + (DrawRect.Right - DrawRect.Left) shr 1 -
(TDBGrid(Sender).Canvas.TextWidth(StrValue) shr 1);
TDBGrid(Sender).Canvas.TextRect(DrawRect, fLeft2,
DrawRect.Top + SPACE_TO_CENTER_CELLTEXT, StrValue);
end;
TDBGrid(Sender).Canvas.Pen.Color := ActualPenColor;
TDBGrid(Sender).Canvas.Brush.Color := ActualBrushColor;
end;
Regards.

Delphi: manually draw list view when horizontal bar is present

I draw list view items with OwnerDraw, but I have bugs: please resize a column -> you will have a horizontal bar, scroll it -> items are drawn on a visible area :(
Please help me to edit a code below. Thanks for your attention and help!!!
Added:
I checked, Rect.Right and Rect.Left increase/decrease while scrolling. E.g. we scroll 50 px right, Rect.Right will be Rect.Right+50, Rect.Left will be -50 (0-50)
A normal view:
Bugs:
procedure TDownloadFrame.DownloadListDrawItem(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 odSelected in State then
begin
Sender.Canvas.Font.Color := clWhite;
Sender.Canvas.Brush.Color := $00FF8000;
end
else
begin
Sender.Canvas.Font.Color := clBlack;
Sender.Canvas.Brush.Color := clWhite;
end;
Sender.Canvas.Brush.Style := bsSolid;
Sender.Canvas.FillRect(Rect);
x1 := 0;
x2 := 0;
R := Rect;
Sender.Canvas.Brush.Style := bsClear;
MainForm.Icons_16x16.Draw(DownloadList.Canvas, 3, R.Top + (R.Bottom - R.Top - 16) div 2, 1, true);
for i := 0 to DownloadList.Columns.Count - 1 do
begin
Inc(x2, ListView_GetColumnWidth(DownloadList.Handle,
DownloadList.Columns[i].Index));
R.Left := x1;
R.Right := x2;
if i = 0 then
begin
s := Item.Caption;
R.Left := 16 + 6;
end
else
s := Item.SubItems[i - 1];
if i <> 3 then
DrawText(Sender.Canvas.Handle, s, length(s), R, DT_SINGLELINE or
DT_ALIGN[DownloadList.Columns[i].Alignment] or DT_VCENTER or
DT_END_ELLIPSIS);
x1 := x2;
end;
end;
Change:
1)
x1 := 0;
x2 := 0;
to
x1 := Rect.Left;
x2 := Rect.Left;
2)
MainForm.Icons_16x16.Draw(DownloadList.Canvas, 3, R.Top + (R.Bottom - R.Top - 16) div 2, 1, true);
to
MainForm.Icons_16x16.Draw(DownloadList.Canvas, R.Left+3, R.Top + (R.Bottom - R.Top - 16) div 2, ImgIndex, true);
3)
if i = 0 then
begin
s := Item.Caption;
R.Left := 16 + 6;
end
to
if i = 0 then
begin
s := Item.Caption;
R.Left := R.Left + 16 + 6;
end
i came across a bug in the VCL recently where if i draw on a ListItem with an imagelist, then the listview's canvas would no longer honor any font color, font size, or font face changes of Sender.Canvas.Font:
Sender.Canvas.Font.Color := clHighlightText;
Sender.Canvas.Font.Size := 14;
Sender.Canvas.Font.Name := 'Consolas';
...none would work. This would only stop working if i first drew on the canvas using:
imageList.Draw(Sender.Canvas, ....);
If i removed the imageList.Draw everything was fine.
i was forced to set the font and colors using GDI directly:
savedDC := SaveDC(Sender.Canvas.Handle);
try
SetTextColor(Sender.Canvas.Handle, clHighlightText); //don't use clWhite, use the correct color
newfont := TFont.Create;
try
newFont.Assign(Sender.Canvas.Font);
newFont.Name := 'Consolas';
newfont.Size := 14;
SelectObject(Sender.Canvas.Handle, newFont.Handle);
szText = 'Hello, world!';
TextOut(Sender.Canvas.Handle, 0, 0, PChar(szText), Length(szText));
finally
newFont.Free;
end;
finally
RestoreDC(Sender.Canvas.Handle, savedDC);
end;
Note: Any code is released into the public domain. No attribution required.

ListView in vsReport mode colouring of Items and rows

I want to color one row in gray and the other in white.
I have the following code but there is white space of vertical lines of columns in Windows 7.
How do I color all rows?
procedure TForm2.Update_ListBoxCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
if Item.Index mod 2=0
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;
end;
Set OwnerDraw to true and add
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 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;
Sender.Canvas.Brush.Style := bsSolid;
Sender.Canvas.FillRect(Rect);
x1 := 0;
x2 := 0;
r := Rect;
Sender.Canvas.Brush.Style := bsClear;
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
S := Item.Caption
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;
end;
In the above example, the first column is left-aligned and the two other are centered.
If you only have one column, that is, no subitems:
procedure TForm1.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
var
r: TRect;
S: string;
const
DT_ALIGN: array[TAlignment] of integer = (DT_LEFT, DT_RIGHT, DT_CENTER);
begin
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;
Sender.Canvas.Brush.Style := bsSolid;
Sender.Canvas.FillRect(Rect);
r := Rect;
Sender.Canvas.Brush.Style := bsClear;
S := Item.Caption;
DrawText(Sender.Canvas.Handle,
S,
length(S),
r,
DT_SINGLELINE or DT_ALIGN[ListView1.Columns[0].Alignment] or DT_VCENTER or DT_END_ELLIPSIS);
end;

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!!!
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;
Notice that the 'horse' line is selected and has keyboard focus.

Resources