Solved

TListView: changing background color for items...

Posted on 2004-10-06
27
2,272 Views
Last Modified: 2012-08-13
Hello All,

I tried to use this code to change background color for items:
---
  with(Sender.Canvas)do begin
    if((Item.Index mod 2)=0)
    then Brush.Color:=clSilver
    else Brush.Color:=clWhite;
  end;
  DefaultDraw:=True;
---

This code normally works, but background colors is not for all item,
I mean that from left side and right side I have a white border...

How can I change this code to delete this border...

I need Listview view like Miranda IM listviews.

Thanks,
Dan
0
Comment
Question by:DanDaemon
  • 12
  • 8
  • 4
  • +2
27 Comments
 

Author Comment

by:DanDaemon
ID: 12238819
Need answer as soon as possible :)
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12239204
I assume that you use the "OnDrawItem" event.

var
  BoundsRectt: TRect;
begin
  BoundsRect := Item.DisplayRect(drSelectBounds);
  if Odd (Item.Index) then
    ListView1.Canvas.Brush.Color := clWhite
  else
    ListView1.Canvas.Brush.Color := clSilver;
  ListView1.Canvas.Brush.Style := bsSolid;
  ListView1.Canvas.FillRect(BoundsRect);
0
 

Author Comment

by:DanDaemon
ID: 12239318
strange, but your code does not work :(

all items are white.
0
Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

 

Author Comment

by:DanDaemon
ID: 12239360
Found bug, but this code has some other problem...

When you will move mouse pointer to some item and will wait, this item will be hiden... I mean text
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12239611
My mistake. Here is the complete code. Remember to set: OwnerDraw := True;

var
  OldBkMode: Integer;
  OldTextColor: TColor;
  i: Integer;
  BoundsRect,LabelRect: TRect;
begin
  BoundsRect := Item.DisplayRect(drSelectBounds);
  if Odd (Item.Index) then
    ListView1.Canvas.Brush.Color := clWhite
  else
    ListView1.Canvas.Brush.Color := clSilver;
  ListView1.Canvas.Brush.Style := bsSolid;
  ListView1.Canvas.FillRect(BoundsRect);

  (*
   * TOwnerDrawState = set of (odSelected, odGrayed, odDisabled, odChecked,
   *  odFocused, odDefault, odHotLight, odInactive, odNoAccel, odNoFocusRect,
   *  odReserved1, odReserved2, odComboBoxEdit);
   *)
  OldBkMode := GetBkMode (ListView1.Canvas.Handle);
  try
    OldTextColor := GetTextColor (ListView1.Canvas.Handle);
    try
      SetBkMode (ListView1.Canvas.Handle, TRANSPARENT);
      if (odSelected in State) then
      begin
        SetBkMode (ListView1.Canvas.Handle, OPAQUE);
        SetBkColor (ListView1.Canvas.Handle, clNavy);
        SetTextColor (ListView1.Canvas.Handle, clWhite);
      end;

      LabelRect := Item.DisplayRect(drLabel);
      OffsetRect (LabelRect, 1,0);
      InflateRect (LabelRect, -1,0);

      DrawText (ListView1.Canvas.Handle, PChar(Item.Caption),length(Item.Caption), LabelRect,
        DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

    finally
      SetTextColor (ListView1.Canvas.Handle, OldTextColor);
    end;

    SetBkMode (ListView1.Canvas.Handle, TRANSPARENT);
    LabelRect := Item.DisplayRect(drBounds);
    i := 0;
    while (i < Item.SubItems.Count) do
    begin
      OffsetRect (LabelRect, Columns[i].Width,0);
      LabelRect.Right := LabelRect.Left + Columns[1+i].Width;

      DrawText (ListView1.Canvas.Handle, PChar(Item.SubItems[i]),length(Item.SubItems[i]),
        LabelRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

      inc (i);
    end;
  finally
    SetBkMode (ListView1.Canvas.Handle, OldBkMode);
  end;
0
 
LVL 6

Expert Comment

by:vadim_ti
ID: 12239612
onDrawItem event not fired for tlistview , at least in delphi7
0
 

Author Comment

by:DanDaemon
ID: 12239767
Ok, this code works partially:

I used it for event OnCustomDrawItem with OwnerDraw in True.

1. These lines could not compile:
---
    while (i < Item.SubItems.Count) do
    begin
      OffsetRect (LabelRect, Columns[i].Width,0);
      LabelRect.Right := LabelRect.Left + Columns[1+i].Width;

      DrawText (ListView1.Canvas.Handle, PChar(Item.SubItems[i]),length(Item.SubItems[i]),
        LabelRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);
      //
      inc (i);
    end;
---

2. When I turned on OwnerDraw to true all items have had a very strange view.
0
 

Author Comment

by:DanDaemon
ID: 12239792
Probably this code is for Delphi version below 7?
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12240037
This is indeed D6. But it also works fine in D7.
I did set:
- ViewStyle to vsReport
- OwnerDraw to True
- RowSelect to True

Those lines contained a small error, here's the corrected part:

    while (i < Item.SubItems.Count) do
    begin
      OffsetRect (LabelRect, ListView1.Columns[i].Width,0);
      LabelRect.Right := LabelRect.Left + ListView1.Columns[1+i].Width;

      DrawText (ListView1.Canvas.Handle, PChar(Item.SubItems[i]),length(Item.SubItems[i]),
        LabelRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

And the complete function again:

procedure TForm1.ListView1DrawItem(Sender: TCustomListView;
  Item: TListItem; Rect: TRect; State: TOwnerDrawState);
var
  OldBkMode: Integer;
  OldTextColor: TColor;
  i: Integer;
  BoundsRect,LabelRect: TRect;
begin
  BoundsRect := Item.DisplayRect(drSelectBounds);
  if Odd (Item.Index) then
    ListView1.Canvas.Brush.Color := clWhite
  else
    ListView1.Canvas.Brush.Color := clSilver;
  ListView1.Canvas.Brush.Style := bsSolid;
  ListView1.Canvas.FillRect(BoundsRect);

  (*
   * TOwnerDrawState = set of (odSelected, odGrayed, odDisabled, odChecked,
   *  odFocused, odDefault, odHotLight, odInactive, odNoAccel, odNoFocusRect,
   *  odReserved1, odReserved2, odComboBoxEdit);
   *)
  OldBkMode := GetBkMode (ListView1.Canvas.Handle);
  try
    OldTextColor := GetTextColor (ListView1.Canvas.Handle);
    try
      SetBkMode (ListView1.Canvas.Handle, TRANSPARENT);
      if (odSelected in State) then
      begin
        SetBkMode (ListView1.Canvas.Handle, OPAQUE);
        SetBkColor (ListView1.Canvas.Handle, clNavy);
        SetTextColor (ListView1.Canvas.Handle, clWhite);
      end;

      LabelRect := Item.DisplayRect(drLabel);
      OffsetRect (LabelRect, 1,0);
      InflateRect (LabelRect, -1,0);

      DrawText (ListView1.Canvas.Handle, PChar(Item.Caption),length(Item.Caption), LabelRect,
        DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

    finally
      SetTextColor (ListView1.Canvas.Handle, OldTextColor);
    end;

    SetBkMode (ListView1.Canvas.Handle, TRANSPARENT);
    LabelRect := Item.DisplayRect(drBounds);
    i := 0;
    while (i < Item.SubItems.Count) do
    begin
      OffsetRect (LabelRect, ListView1.Columns[i].Width,0);
      LabelRect.Right := LabelRect.Left + ListView1.Columns[1+i].Width;

      DrawText (ListView1.Canvas.Handle, PChar(Item.SubItems[i]),length(Item.SubItems[i]),
        LabelRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

      inc (i);
    end;
  finally
    SetBkMode (ListView1.Canvas.Handle, OldBkMode);
  end;
end;

0
 

Author Comment

by:DanDaemon
ID: 12240124
Ok, thanks, but this code does not work in Delphi 7.

Same error with OffsetRect (LabelRect, ListView1.Columns[i].Width,0);

ERROR: Undeclared idintifier: 'Columns'
0
 
LVL 6

Expert Comment

by:vadim_ti
ID: 12240241
Bart:

fine
row select was really missing
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12240843
This code was taken from a custom listview I created. Since I only needed to draw normal and focused items I never bothered to draw all the other states. If you want the other states it might take some work and recoding to get them all painted properly.
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 12242877
I put together some example for you to build on:

Source file:
~~~~~~~

unit main;

interface

uses
  Windows, Classes, Graphics, Controls, Forms,
  ComCtrls, StdCtrls, ExtCtrls, ImgList;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    RadioGroup1: TRadioGroup;
    ImageList1: TImageList;
    procedure RadioGroup1Click(Sender: TObject);
    procedure ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
      Rect: TRect; State: TOwnerDrawState);
  end;

var
  Form1: TForm1;

implementation

uses Types;

{$R *.dfm}

procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  Case Radiogroup1.ItemIndex of
    0: Listview1.ViewStyle:= vsIcon;
    1: Listview1.ViewStyle:= vsList;
    2: Listview1.ViewStyle:= vsReport;
    3: Listview1.ViewStyle:= vsSmallIcon;
  end;
end;

procedure TForm1.ListView1DrawItem(Sender: TCustomListView;
  Item: TListItem; Rect: TRect; State: TOwnerDrawState);
var r: TRect;
    i: integer;
begin
  with(Sender.Canvas)do begin
    if((Item.Index mod 2)=0)
    then Brush.Color:=clSilver
    else Brush.Color:=clWhite;
  end;
  Sender.Canvas.FillRect(Rect);
  ImageList1.Draw(Sender.Canvas, Rect.Left, Rect.Top, Item.ImageIndex);
  r:= Rect;
  r.Right:= Sender.Column[0].Width;
  r.Left:= Rect.Left+ImageList1.Width+5;
  Sender.Canvas.TextRect(r,r.Left,r.Top,Item.Caption);

  for i:= 0 to Item.SubItems.Count-1 do
  begin
    if (i+1 <= TListView(Sender).Columns.Count) then
    begin
      r.Left:= r.Right;
      r.Right:= r.Left + Sender.Column[i+1].Width;
      ImageList1.Draw(Sender.Canvas, R.Left+2, R.Top, Item.SubItemImages[i]);
      r.Left:= r.Left + ImageList1.Width + 5;
      Sender.Canvas.TextRect(r,r.Left,r.Top, Item.SubItems[i]);
    end;
  end;

  if odFocused in State then
  begin
    Sender.Canvas.Brush.Color:= clRed;
    Sender.Canvas.FrameRect(Rect);
  end;
end;

end.


DFM File:
~~~~~~

object Form1: TForm1
  Left = 192
  Top = 114
  Width = 696
  Height = 480
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object ListView1: TListView
    Left = 72
    Top = 64
    Width = 250
    Height = 121
    Columns = <
      item
      end
      item
      end
      item
      end>
    Items.Data = {
      EA0000000700000000000000FFFFFFFFFFFFFFFF000000000000000005497465
      6D3101000000FFFFFFFFFFFFFFFF0200000000000000054974656D3208537562
      4974656D31085375624974656D3202000000FFFFFFFFFFFFFFFF020000000000
      0000054974656D33085375624974656D31085375624974656D3203000000FFFF
      FFFFFFFFFFFF0000000000000000054974656D3404000000FFFFFFFFFFFFFFFF
      0000000000000000054974656D3505000000FFFFFFFFFFFFFFFF000000000000
      0000054974656D3606000000FFFFFFFFFFFFFFFF000000000000000005497465
      6D37FFFFFFFF00000100}
    LargeImages = ImageList1
    OwnerDraw = True
    SmallImages = ImageList1
    TabOrder = 0
    OnDrawItem = ListView1DrawItem
  end
  object RadioGroup1: TRadioGroup
    Left = 24
    Top = 240
    Width = 185
    Height = 105
    Caption = 'RadioGroup1'
    ItemIndex = 0
    Items.Strings = (
      'Icons'
      'List'
      'Report'
      'Small Icon')
    TabOrder = 1
    OnClick = RadioGroup1Click
  end
  object ImageList1: TImageList
    Left = 216
    Top = 24
    Bitmap = {
(* YOU MUST ADD YOUR OWN PICTURES *)
      }
  end
end



Kind Regards
Pierre
0
 

Author Comment

by:DanDaemon
ID: 12246530
Hello Pierre,

Was you resizing columns after launch your example?
Try it and you will see problems with your code.

Thanks,
Dan

P.S. I need code which is worked on 100% with Delphi 7.
0
 
LVL 1

Accepted Solution

by:
Bart_Thomas earned 400 total points
ID: 12251421
Okay, here's the entire test-unit I made
from my "component".

Regards
Bart Thomas

unit1.pas

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListView1: TListView;
    procedure ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
      Rect: TRect; State: TOwnerDrawState);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ListView1DrawItem(Sender: TCustomListView;
  Item: TListItem; Rect: TRect; State: TOwnerDrawState);
var
  OldBkMode: Integer;
  OldTextColor: TColor;
  i: Integer;
  BoundsRect,LabelRect: TRect;
begin
  BoundsRect := Item.DisplayRect(drSelectBounds);
  if Odd (Item.Index) then
    ListView1.Canvas.Brush.Color := clWhite
  else
    ListView1.Canvas.Brush.Color := clSilver;
  ListView1.Canvas.Brush.Style := bsSolid;
  ListView1.Canvas.FillRect(BoundsRect);

  (*
   * TOwnerDrawState = set of (odSelected, odGrayed, odDisabled, odChecked,
   *  odFocused, odDefault, odHotLight, odInactive, odNoAccel, odNoFocusRect,
   *  odReserved1, odReserved2, odComboBoxEdit);
   *)
  OldBkMode := GetBkMode (ListView1.Canvas.Handle);
  try
    OldTextColor := GetTextColor (ListView1.Canvas.Handle);
    try
      SetBkMode (ListView1.Canvas.Handle, TRANSPARENT);
      if (odSelected in State) then
      begin
        SetBkMode (ListView1.Canvas.Handle, OPAQUE);
        SetBkColor (ListView1.Canvas.Handle, clNavy);
        SetTextColor (ListView1.Canvas.Handle, clWhite);
      end;

      LabelRect := Item.DisplayRect(drLabel);
      OffsetRect (LabelRect, 1,0);
      InflateRect (LabelRect, -1,0);

      DrawText (ListView1.Canvas.Handle, PChar(Item.Caption),length(Item.Caption), LabelRect,
        DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

    finally
      SetTextColor (ListView1.Canvas.Handle, OldTextColor);
    end;

    SetBkMode (ListView1.Canvas.Handle, TRANSPARENT);
    LabelRect := Item.DisplayRect(drBounds);
    i := 0;
    while (i < Item.SubItems.Count) do
    begin
      OffsetRect (LabelRect, ListView1.Columns[i].Width,0);
      LabelRect.Right := LabelRect.Left + ListView1.Columns[1+i].Width;

      DrawText (ListView1.Canvas.Handle, PChar(Item.SubItems[i]),length(Item.SubItems[i]),
        LabelRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS);

      inc (i);
    end;
  finally
    SetBkMode (ListView1.Canvas.Handle, OldBkMode);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  ListItem: TListItem;
  i: Integer;
begin
  for i := 0 to 20 do
  begin
    ListItem := ListView1.Items.Add;
    ListItem.Caption := Format ('Caption(%d)', [i]);
    ListItem.SubItems.Add(Format ('Sub 1 (%d)', [i]));
    ListItem.SubItems.Add(Format ('Sub 2 (%d)', [i]));
    ListItem.SubItems.Add(Format ('Sub 3 (%d)', [i]));
  end;
end;

end.

unit1.dfm

object Form1: TForm1
  Left = 189
  Top = 107
  Width = 870
  Height = 640
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object ListView1: TListView
    Left = 24
    Top = 8
    Width = 649
    Height = 545
    Columns = <
      item
        Caption = 'Column 1'
        Width = 100
      end
      item
        Caption = 'Column 2'
        Width = 100
      end
      item
        Caption = 'Column 3'
        Width = 100
      end
      item
        Caption = 'Column 4'
        Width = 100
      end>
    OwnerDraw = True
    RowSelect = True
    TabOrder = 0
    ViewStyle = vsReport
    OnDrawItem = ListView1DrawItem
  end
end
0
 

Author Comment

by:DanDaemon
ID: 12251454
hehe... Thank you Bart_Thomas,

But your code still has some problems, try to resize first column.

Thanks,
Dmitry
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12251738
Weird, there's nothing wrong here. I made the orginal code in D5 and tested it in D6 and D7. It works fine in all versions.
What exactly happes when you resize the first column?

Regards,
Bart Thomas
0
 

Author Comment

by:DanDaemon
ID: 12251881
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12252128
WOW! How did that happen.
Weirdly enough I can resize all I want and everything paints perfectly.

What Delphi version are you using? Paste your code please, so I can test it here.
Who knows, maybe there's a small difference somewhere between our versions.

Regards,
Bart Thomas
0
 

Author Comment

by:DanDaemon
ID: 12252232
WinXP+SP2 :)
D7+SP1+SP2
0
 
LVL 1

Expert Comment

by:Bart_Thomas
ID: 12252543
I have Win 2K and a regular D7. I don't think the service packs are the reason, or at least not very likely. More likely is Win XP. I've see very weird things with code that works fine in 2K and screws up in XP. Maybe in XP the new columnwidth isn't set. So the next lines are the problem:

      OffsetRect (LabelRect, ListView1.Columns[i].Width,0);
      LabelRect.Right := LabelRect.Left + ListView1.Columns[1+i].Width;

Can you check if the width of the columns changes after you resize them?

Regards,
Bart Thomas
0
 

Expert Comment

by:joncmora
ID: 12255710
This works for me in a lot of projects. You should use OnCustomDrawItem.

procedure TfrmMain.lstUsersCustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  if Odd(Item.Index) then
    Sender.Canvas.Brush.Color := $00F5F5F5
  else
    Sender.Canvas.Brush.Color := clWindow;
end;
0
 

Expert Comment

by:joncmora
ID: 12255711
OwnerDraw should be set to False
0
 

Author Comment

by:DanDaemon
ID: 12256922
2 joncmora,

Do you see difference between your method and what I need?
If not, please re-read this topic again.

Thanks,
Dan.
0
 

Expert Comment

by:joncmora
ID: 12264732
Ops!

"I mean that from left side and right side I have a white border..."

I missed this part... hehehe...
0
 

Expert Comment

by:joncmora
ID: 12265410
OK, here you go...
EVENT: OnCustomDrawItem

-------------------------------------------
procedure TfrmMain.lstUsersCustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
var
  ItemRect: TRect;
begin
  if not (cdsSelected in State) then
  begin
    if (Item.Index mod 2) = 0 then
      Sender.Canvas.Brush.Color := clSilver
    else
      Sender.Canvas.Brush.Color := clWhite {do not use the same value as lstUsers.Color, clWindow looks weird...};
  end
  else
    Sender.Canvas.Brush.Color := clActiveCaption;
  ItemRect := Item.DisplayRect(drLabel);
  ItemRect.Left := ItemRect.Left - 2 {This may not be correct in another resolution, I'm using 1024x768};
  Sender.Canvas.FillRect(ItemRect);
end;
0
 

Author Comment

by:DanDaemon
ID: 12265435
2joncmora,

Thank you very much, but now you can move your cursor to this iteam and see what will happens when cursor will be there :)
Same problems that for Bart_Thomas code :(

Thanks,
Dan
0

Featured Post

ScreenConnect 6.0 Free Trial

Explore all the enhancements in one game-changing release, ScreenConnect 6.0, based on partner feedback. New features include a redesigned UI, app configurations and chat acknowledgement to improve customer engagement!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Along with being a a promotional video for my three-day Annielytics Dashboard Seminor, this Micro Tutorial is an intro to Google Analytics API data.
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question