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...
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.
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.