Link to home
Start Free TrialLog in
Avatar of snoopy_Spy
snoopy_Spy

asked on

ProgressBar in ListView

How can i (the easiest way) show a Progress bar (like the TProgressBar) in a Column of a ListView (with Re-Moving and Re-Sizeing)
Avatar of philipleighs
philipleighs

Hi,

The easiest way would be to position a progress bar over the top of the listview's column.
Change the position/column at run time.

....but the approach I would take is:

Create a descendant of TListView. Inside, create a contained progressbar. Override the Paint method, implement the function with a call to inherited, then FProgressBar.PaintTo(Canvas, X, Y);

Cheers,
Phil.
Avatar of snoopy_Spy

ASKER

But how can i get the x and y - positions of the progress bar ? (for example column 3 and row 6 )
Hi again,

Try this. Make sure that Col and Row are not outside your range, or you'll get a range error.
Also, it assumes that the ListView is in vsReport mode.
ShowColumnHeaders can be true or false.

procedure TForm1.RepositionProgressBar(Col, Row: Integer);
var pt: TPoint;
    RowHeight: Integer;
    ColLeft: Integer;
    Index: Integer;
    rc: TRect;
  begin
    //Work out the top left position of the first list item
    pt := ListView1.Items[0].Position;

    //Work out the row height
    RowHeight := ListView1.Items[1].Position.Y - pt.Y;

    //Work out the left edge of the specified column
    ColLeft := 0;
    for Index := 0 to Col - 1 do
      Inc(ColLeft, ListView1.Column[Index].Width);

    //Build the bounding rectangle, and reposition the progress bar
    rc.Left := ListView1.Left + ColLeft + pt.X;
    rc.Top := ListView1.Top + RowHeight * Row + pt.Y;
    rc.Right := rc.Left + ListView1.Column[Col].Width;
    rc.Bottom := rc.Top + RowHeight;
    ProgressBar1.BoundsRect := rc;
  end;

Cheers,
Phil.
BTW, it needs to have at least two items in the list view (otherwise it can't workout the rowheight).

Cheers,
Phil.
Hey snoopy,

Have you had a chance to try this out?

Cheers,
Phil.
I have 2 problems with your solution :
1.) I can't change the Background auf the TProgressBar to the Background of the Listview (not so important)
but 2.)
I can't write a event where i can change the position and size of the ProgressBar when i move or resize the column !
The parent form gets a WM_NOTIFY while a column's width is changing.

Each time your form gets a WMNotify message, look at the idCtrl parameter (wParam).

If it equals the handle of the listview, then recalculate the bounds of the progress bar. (you could narrow down the number of times you check by examining the contents of lParam (address of an NMHDR).

If the rectangle is different from the progress bar's position, then use BoundsRect to change it.

Hmm, I don't know about the background of the progress bar.

Cheers,
Phil.
What do you mean with the lParam (address of an NMHDR) ?
What is a NMHDR ?
It's a windows type. It is described in the Win32 API help. TWMNotify exposes it so you don't have to do any typecasting.

Basically, declare a message handler in the form.

TForm1 = class(TForm)
....
protected
  procedure WMNotify(var Msg: TWMNotify); message WM_NOTIFY;

....

implementation

....

procedure TForm1.WMNotify(var Msg: TWMNotify);
begin
  inherited;
  if Msg.idCtrl = ListView1.Handle then
    if Msg.NMHdr.code = xxxx {some magic number} then
      begin
        ResizeProgressBar;
      end;
end;


if you omit this line:
    if Msg.NMHdr.code = xxxx {some magic number} then

you'll resize the progress bar everytime something happens to the listview which would be overkill. My guess is that NMHdr.code will contain a particular value when you resize a column. Work out that value and hard code it.

I haven't actually done this, but it's an approach that I'd take.

One other thing, this line:
  if Msg.idCtrl = ListView1.Handle then

might need to be changed to:
  if Msg.idCtrl = GetDlgCtrlID(ListView1.Handle) then

Good luck,
Phil.
I have tried this :
procedure TForm1.WMNotify(var Msg: TWMNotify);
begin
    inherited;
    if Msg.idCtrl = ListView1.Handle then
        Edit2.Text := 'YES'
    else Edit2.Text := 'NO';

But i only get YES when i click the column header or on a item not when i change the column width !
Hmm, when I looked in Spy++ it showed me that WM_NOTIFY was being sent repeatedly when the mouse was down and moving over the column.

BTW, how you you know that it is only sent when you click? You could be setting Edit2.Text to 'YES' 50 times.

A method I use for this kind of thing is:

if Msg.idCtrl = ListView1.Handle then
begin
  Caption := Caption + 'x';
  if Length(Caption) > 50 then
    Caption := '';
end;

That way it is more obvious when a message has come through.
Ups ...

thanxs

Post an answer !
ASKER CERTIFIED SOLUTION
Avatar of philipleighs
philipleighs

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
BTW, you can change the background colour of the ProgressBar by putting it inside a panel. ProgressBar uses the background colour of it's parent.

So drop a TPanel on your form, set Color to clWindow, and Visible to false.
Now cut and paste the ProgressBar into the TPanel!

Cheers,
Phil.
Thanxs