Solved

TListView: changing background color for items...

Posted on 2004-10-06
27
2,207 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
Comment Utility
Need answer as soon as possible :)
0
 
LVL 1

Expert Comment

by:Bart_Thomas
Comment Utility
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
Comment Utility
strange, but your code does not work :(

all items are white.
0
 

Author Comment

by:DanDaemon
Comment Utility
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
Comment Utility
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
Comment Utility
onDrawItem event not fired for tlistview , at least in delphi7
0
 

Author Comment

by:DanDaemon
Comment Utility
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
Comment Utility
Probably this code is for Delphi version below 7?
0
 
LVL 1

Expert Comment

by:Bart_Thomas
Comment Utility
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
Comment Utility
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
Comment Utility
Bart:

fine
row select was really missing
0
 
LVL 1

Expert Comment

by:Bart_Thomas
Comment Utility
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
Comment Utility
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
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 

Author Comment

by:DanDaemon
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
0
 
LVL 1

Expert Comment

by:Bart_Thomas
Comment Utility
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
Comment Utility
WinXP+SP2 :)
D7+SP1+SP2
0
 
LVL 1

Expert Comment

by:Bart_Thomas
Comment Utility
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
Comment Utility
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
Comment Utility
OwnerDraw should be set to False
0
 

Author Comment

by:DanDaemon
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
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…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

762 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

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now