1. Home
  2. Computing & Technology
  3. Delphi Programming
Placing a TProgressBar into a TListView
Here's how to add a progress bar (or any other Delphi component) to a ListView control. Plus: full source code to the TListViewEx component (TListView descendant) with ColumnResize events!
 DOWNLOAD:
• Sample Project
• TListViewEx with Source
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• ProgressBar in StatusBar
• Building custom components
• Using VCL components
• Free Source components

Most download managers use a list view type of component to provide visual feedback of the amount of remaining download per item. Delphi's TListView component displays a list of items in various ways.
A TProgressBar displays a simple progress bar. Progress bars provide users with visual feedback about the progress of a procedure within an application.

Here's how to place a progress bar into a column of a list view box...

Win32 Delphi Palette

ProgressBar in ListView
When the ViewStyle property of a ListView control is set to vsReport, each item appears on its own line with information arranged in columns. The leftmost column contains the small icon and label, and subsequent columns contain subitems as specified by the application.
You are probably looking at this every day ... when using Windows Explorer in Details view.

Suppose you have a ListView on a form with ViewStyle set to vsReport. Two columns are defined using the Columns property: the first column holds the name of the item, the second column should store a progress indicator.

ProgressBar in ListView column

When the "AddItem" button is clicked a new item should be added to the list with a progress bar:

procedure TForm1.AddItemButtonClick(
  Sender: TObject);
const
  pbColumnIndex = 1;
  pbMax = 100;
var
  li : TListItem;
  lv : TListView;
  pb : TProgressBar;
  pbRect : TRect;
begin
  lv := ListViewEx1;

  //create new ListItem (for the lv)
  li := lv.Items.Add;
  li.Caption := 'Item ' + IntToStr(lv.Items.Count);

  //create a ProgressBar, place it in the second column
  pb := TProgressBar.Create(nil);
  pb.Parent := lv;
  li.Data := pb;
  pbRect := li.DisplayRect(drBounds);
  pbRect.Left := pbRect.Left + 
                 lv.Columns[-1 + pbColumnIndex].Width;
  pbRect.Right := pbRect.Left + 
                  lv.Columns[pbColumnIndex].Width;
  pb.BoundsRect := pbRect;
end; //AddItemButtonClick

When the AddItemButton is clicked a new list item (TListItem) is added to the ListView control (named ListViewEx1). A progress bar is created, a reference to the progress bar is added into the Data property of the list item, and finally, progress bar is placed in the column specified by the pbColumnIndex. Some calculations are used to make the progress bar appear at the right place.

When you want to remove an item from the list, you need to make sure the "attached" progress bar's memory is freed, and all the progress bars below the selected one are moved one position to the top (as this is what happens to all the remaining items of a ListView):

procedure TForm1.RemoveItemButtonClick(
  Sender: TObject);
var
  lv : TListView;
  li : TListItem;
  i, idx : integer;
  pb : TProgressBar;
begin
  lv := ListViewEx1;

  li := lv.Selected;

  if li <> nil then
  begin
    idx := li.Index;
    TProgressBar(li.Data).Free;
    lv.Items.Delete(idx);

    //move bars up
    for i := idx to -1 + lv.Items.Count do
    begin
      li := lv.Items.Item[i];
      pb := TProgressBar(li.Data);
      pb.Top := pb.Top - 
                (pb.BoundsRect.Bottom - 
                 pb.BoundsRect.Top);
    end;
  end;
end; //RemoveItemButtonClick

Just for testing, we'll add some dummy code inside a TTimer's OnTime event to have some progress in bars (drop a TTimer component on a form, and use the next code for the OnTime event). Obviously, in real applications you would update the progress bar of an item based on some criteria - you only need to locate "its" progress bar ("stored" in the Data property of an item):

procedure TForm1.Timer1Timer(
  Sender: TObject);
var
  idx : integer;
  pb: TProgressbar;
  lv : TListView;
begin
  lv := ListViewEx1;

  if lv.Items.Count = 0 then Exit;
  
  //randomly pick an item and 
  //increment it's progress position 
  idx := Random(lv.Items.Count);
  pb := TProgressBar(lv.Items[idx].Data);
  if pb.Position < pb.Max then
    pb.StepIt
  else
    pb.Position := 0;
end;//Timer1Timer

Run the appication, hit the "AddItem" button several times, and watch the progress going on :)

Now, try to resize any of the columns ... :( uuoopps!
When you resize the columns you need to reposition the progress bars, but TListView does not provide any event to handle resizing of columns!

TListViewEx - ListView with column resize events
You solution to handling the "column resizing" situation is in using a TListView derived control: TListViewEx. The TListViewEx is a TListView descendant with BeginColumnResize, ColumnResize and EndColumnResize events published.

I've located the source to TListViewEx control somewhere on news:borland.*. The code was provided by Peter Below (a TeamB member).

Now, with the power of TListViewEx, you can easily handle the user_is_resizing_columns situation. Here's how to handle the OnEndColumnResize event to adjust the width and left bounds of the progress bars:

procedure TForm1.ListViewEx1EndColumnResize(
  sender: TCustomListView;
  columnIndex,
  columnWidth: Integer);
var
  lv : TListViewEx;
  idx : integer;
  pb : TProgressBar;
begin
  lv := ListViewEx1;

  //first column
  if columnIndex = 0 then
  begin
    for idx := 0 to -1 + lv.Items.Count do
    begin
      pb := TProgressBar(lv.Items[idx].Data);
      pb.Left := columnWidth;
    end;
  end;

  //progress bar column
  if columnIndex = 1 then
  begin
    for idx := 0 to -1 + lv.Items.Count do
    begin
      pb := TProgressBar(lv.Items[idx].Data);
      pb.Width := columnWidth;
    end;
  end;
end;

TListViewEx - Source
First, download the component. The TListViewEx comes as a single unit file (.pas extension). You'll need to add the component into an existing package. Here's "How to Install Custom Component in Delphi (into Existing Package)"
Questions? Comments? Extensions? Exceptions?!
That's it. As always if there are any questions or comments please post them on the Delphi Programming Forum.

Explore Delphi Programming
About.com Special Features

Holiday Central

What to eat, where to go, fun things to do and how to save money on the perfect gifts. More >

Family Tech Center

Stay connected and entertained with reviews on tips on the latest HDTVs, cellphones and more. More >

  1. Home
  2. Computing & Technology
  3. Delphi Programming

©2009 About.com, a part of The New York Times Company.

All rights reserved.