Placing a TProgressBar into a TListView

来源:互联网 发布:年月日时与五行算法 编辑:程序博客网 时间:2024/05/11 04:17

 

 

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.