Thanks, Jerry
Take a look at the Delphi Pool at http://www.lmc-mediaagentur.de/dpool.htm
Under Delphi -> Tips & Tricks -> VCL -> TListView -> 0006 you will find what
you're looking for.
HTH
Uwe
There are some gotchas here: the DisplayRect method of a listitem does
not return the correct position unless the control is visible, thus the
hack used at the top of the method to ensure this. And of course you will
have to adjust the width and left bound of the progress bars if the user
resizes columns. And you need to add new bars if the user can add items
and destroy bars if the user can delete item.
procedure TForm1.FormCreate(Sender: TObject);
var
pb: TProgressBar;
r: TRect;
i, k: Integer;
begin
Show;
Application.ProcessMessages;
for i:= 0 To listview1.items.count-1 do begin
r:= listview1.items[i].DisplayRect( drBounds );
// last column is to take progress bar
for k:= 1 to listview1.columns.Count -1 do
r.left := r.left + listview1.columns[k-1].Width;
r.right := r.Left +
listview1.columns[listview1.columns.Count -1].Width;
pb:= TProgressBar.Create( self );
pb.Parent := listview1;
pb.BoundsRect := r;
listview1.items[i].Data := pb;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
i: Integer;
pb: TProgressbar;
begin
i:= Random( listview1.items.count );
pb:= TProgressBar( listview1.Items[i].Data );
If assigned(pb) then
if pb.Position = pb.Max Then
pb.Position := 0
else
pb.StepBy( pb.Max div 10 );
end;
Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitly requested!
Note: I'm unable to visit the newsgroups every day at the moment,
so be patient if you don't get a reply immediately.
Thanks.
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006d4...@antispam.compuserve.com...
The event can be added with a bit of work. See the custom listview derivative
below. In my tests the OnBeginColumnResize event was not fired (Win95B,
ComCtl32 version 5.8) for some reason but the OnEndColumnResize event does
fire.
unit PBExListview;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls;
type
TLVColumnResizeEvent = Procedure( sender: TCustomListview;
columnindex: Integer ) of Object;
TPBExListview = class(TListview)
private
FBeginColumnResizeEvent: TLVColumnResizeEvent;
FEndColumnResizeEvent : TLVColumnResizeEvent;
Procedure DoBeginColumnResize( columnindex: Integer ); virtual;
Procedure DoEndColumnResize( columnindex: Integer ); virtual;
protected
Procedure WMNotify( Var msg: TWMNotify ); message WM_NOTIFY;
Function FindColumnIndex( pHeader: pNMHdr ): Integer;
published
property OnBeginColumnResize: TLVColumnResizeEvent
read FBeginColumnResizeEvent
write FBeginColumnResizeEvent;
property OnEndColumnResize: TLVColumnResizeEvent
read FEndColumnResizeEvent
write FEndColumnResizeEvent;
end;
procedure Register;
implementation
uses CommCtrl;
procedure Register;
begin
RegisterComponents('PBGoodies', [TPBExListview]);
end;
procedure TPBExListview.DoBeginColumnResize( columnindex: Integer );
begin
If Assigned( FBeginColumnResizeEvent ) Then
FBeginColumnResizeEvent( self, columnindex );
end;
procedure TPBExListview.DoEndColumnResize( columnindex: Integer );
begin
If Assigned( FEndColumnResizeEvent ) Then
FEndColumnResizeEvent( self, columnindex );
end;
function TPBExListview.FindColumnIndex(pHeader: pNMHdr): Integer;
var
hwndHeader: HWND;
iteminfo: THdItem;
itemindex: Integer;
buf: Array [0..128] of Char;
begin
Result := -1;
hwndHeader := pHeader^.hwndFrom;
itemindex := pHDNotify( pHeader )^.Item;
FillChar( iteminfo, sizeof( iteminfo ), 0);
iteminfo.Mask := HDI_TEXT;
iteminfo.pszText := buf;
iteminfo.cchTextMax := sizeof(buf)-1;
Header_GetItem( hwndHeader, itemindex, iteminfo );
If CompareStr( Columns[itemindex].Caption, iteminfo.pszText ) = 0 Then
Result := itemindex
Else Begin
For itemindex := 0 to Columns.count-1 Do
If CompareStr( Columns[itemindex].Caption, iteminfo.pszText ) = 0 Then
Begin
Result := itemindex;
Break;
End;
End;
end;
procedure TPBExListview.WMNotify(var msg: TWMNotify);
begin
inherited;
Case msg.NMHdr^.code Of
HDN_ENDTRACK: DoEndColumnResize( FindColumnIndex( msg.NMHdr ));
HDN_BEGINTRACK: DoBeginColumnResize( FindColumnIndex( msg.NMHdr ));
End;
end;
end.
Jerry
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006d5...@antispam.compuserve.com...
You could check if the mouse button is down (GetKeyState( VK_LBUTTON ) < 0) in
the routine that updates the progress bar position. If it is down check where
the mouse is (GetCursorPos) and do not update the bar if the mouse is over the
listview.
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006d6...@antispam.compuserve.com...
You tell me, you are updating it, not I <g>. If the bar is updated from
a timer for instance it might be updated while a column resize is in
progress.
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006db...@antispam.compuserve.com...
That should be doable. There is a special message, WM_SETREDRAW, that can be
send to a control to disable or enable the automatic redrawing. See win32.hlp.
Not that you have to manually invalidate the control to force a redraw after
enabling redraing again, this is not automatic.
(but on second thought maybe this isn't such a good idea
> becuase if the column before it is being updated it will need to be moved to
> the right). What I would really like to do is make the progress bar update
> correctly DURING a column resize (like it does in Napster).
Mmh, for that one would need to handle one additional notification from the
header control: HDN_TRACK. This one is send to the listview on each move of
the divider line the user is dragging. TCustomListview handles this
notification to update the column width, so you can just call the inherited
handler first and then access the Columns[i].Width to adjust the progress bar
width.
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006dc...@antispam.compuserve.com...
Using the example listview descendent i posted near the start of this thread
you would just extend the WMNotify method:
procedure TPBExListview.WMNotify(var msg: TWMNotify);
begin
inherited;
Case msg.NMHdr^.code Of
HDN_ENDTRACK: DoEndColumnResize( FindColumnIndex( msg.NMHdr ));
HDN_BEGINTRACK: DoBeginColumnResize( FindColumnIndex( msg.NMHdr ));
HDN_TRACK: DoUpdateColumn( FindColumnIndex( msg.NMHdr ));
End;
end;
The (new) DoUpdateColumn method would perhaps fire a new event to allow the
listviews user to react to the column sizing.
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006dd...@antispam.compuserve.com...
Pity. Sue Bill Gates, it's his listview <g>.
I found a MSKB article that is relevant to the problem, i think:
INFO: HDN_TRACK Notifications and Full Window Drag Style
ID: Q183258
Go read it on msdn.microsoft.com. You should be able to fix the problem by
doing this on the listview, e.g. in the forms OnCreate method.
var
wnd: HWND;
wnd:= GetWindow( listview1.handle, GW_CHILD );
SetWindowLong( wnd, GWL_STYLE,
GetWindowLong( wnd, GWL_STYLE ) and not HDS_FULLDRAG );
HDS_FULLDRAG should be declared in unit CommCtrl.
Oh, a better way to do this would be to override the CreateWnd method of the
listview derivative and change the style there after the inherited call.
The drawback is that you can no longer reorder the columns via drag&drop if
the HDS_FULLDRAG style is not set. I have modified the TPBExListview class
accordingly and it works in my system:
unit PBExListview;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls;
type
TLVColumnResizeEvent = Procedure( sender: TCustomListview;
columnindex: Integer ) of Object;
TPBExListview = class(TListview)
private
FBeginColumnResizeEvent: TLVColumnResizeEvent;
FEndColumnResizeEvent : TLVColumnResizeEvent;
FColumnResizeEvent : TLVColumnResizeEvent;
Procedure DoBeginColumnResize( columnindex: Integer ); virtual;
Procedure DoEndColumnResize( columnindex: Integer ); virtual;
Procedure DoColumnResize( columnindex: Integer ); virtual;
protected
Procedure WMNotify( Var msg: TWMNotify ); message WM_NOTIFY;
Function FindColumnIndex( pHeader: pNMHdr ): Integer;
Procedure CreateWnd; override;
published
property OnBeginColumnResize: TLVColumnResizeEvent
read FBeginColumnResizeEvent
write FBeginColumnResizeEvent;
property OnEndColumnResize: TLVColumnResizeEvent
read FEndColumnResizeEvent
write FEndColumnResizeEvent;
property OnColumnResize: TLVColumnResizeEvent
read FColumnResizeEvent
write FColumnResizeEvent;
end;
procedure Register;
implementation
uses CommCtrl;
procedure Register;
begin
RegisterComponents('PBGoodies', [TPBExListview]);
end;
procedure TPBExListview.DoBeginColumnResize( columnindex: Integer );
begin
If Assigned( FBeginColumnResizeEvent ) Then
FBeginColumnResizeEvent( self, columnindex );
end;
procedure TPBExListview.DoEndColumnResize( columnindex: Integer );
begin
If Assigned( FEndColumnResizeEvent ) Then
FEndColumnResizeEvent( self, columnindex );
end;
procedure TPBExListview.DoColumnResize( columnindex: Integer );
begin
If Assigned( FColumnResizeEvent ) Then
FColumnResizeEvent( self, columnindex );
end;
procedure TPBExListview.WMNotify(var msg: TWMNotify);
begin
inherited;
Case msg.NMHdr^.code Of
HDN_ENDTRACK: DoEndColumnResize( FindColumnIndex( msg.NMHdr ));
HDN_BEGINTRACK: DoBeginColumnResize( FindColumnIndex( msg.NMHdr ));
HDN_TRACK: DoColumnResize( FindColumnIndex( msg.NMHdr ));
End;
end;
Procedure TPBExListview.CreateWnd;
var
wnd: HWND;
begin
inherited;
wnd:= GetWindow( handle, GW_CHILD );
SetWindowLong( wnd, GWL_STYLE,
GetWindowLong( wnd, GWL_STYLE ) and not HDS_FULLDRAG );
end;
end.
Thanks, Jerry
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006de...@antispam.compuserve.com...
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006de...@antispam.compuserve.com...
you are going to need asbestos underwear soon if you don't stop this wholesale
quoting of lengthy posts!
The HDN_TRACK notification comes with a record that contains information about
the dragged column. Or you can send directly query the header control about
the column size using the item index that comes with the message.
Header_GetItemRect would be the easiest way to do that.
In analogy to TPBExListview.FindColumnIndex one could add a method
function TPBExListview.FindColumnWidth(pHeader: pNMHdr): Integer;
var
hwndHeader: HWND;
itemindex: Integer;
rect: TRect;
begin
Result := -1;
hwndHeader := pHeader^.hwndFrom;
itemindex := pHDNotify( pHeader )^.Item;
Header_GetItemRect( hwndHeader, itemindex, @rect );
Result := rect.right - rect.left;
end;
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006e0...@antispam.compuserve.com...
"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.00006e0...@antispam.compuserve.com...
No, i leave the hard work to you <g>.
> In other words, it doesn't seem to be
> working any differently than the columns.width property of
> the control.
@#*%%!! OK, one last chance: check if the notification record that
comes with the HDN_TRACK notification contains a valid pointer to a
HD_ITEM record. If so this record may contain more up-to-date
information. The docs are not clear about whether the pointer is
supposed to be valid for this notification. Lets see, that would look
like this:
function TPBExListview.FindColumnWidth(pHeader: pNMHdr): Integer;
begin
Result := -1;
If Assigned( PHDNotify( pHeader )^.pItem ) AND
((PHDNotify( pHeader )^.pItem^.mask and HDI_WIDTH) <> 0 )
Then
Result := PHDNotify( pHeader )^.pItem^.cxy;
end;
Go test that. Oh, and you are *still* quoting full messages...
Will wonders never cease <g>. OK, for the benefit of all you lurkers
who've followed this thread with bated breath, here is the final
version of the TPBExListview control:
unit PBExListview;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
ComCtrls;
type
TLVColumnResizeEvent = Procedure( sender: TCustomListview;
columnindex: Integer;
columnwidth: Integer ) of Object;
TPBExListview = class(TListview)
private
FBeginColumnResizeEvent: TLVColumnResizeEvent;
FEndColumnResizeEvent : TLVColumnResizeEvent;
FColumnResizeEvent : TLVColumnResizeEvent;
protected
Procedure DoBeginColumnResize( columnindex, columnwidth: Integer );
virtual;
Procedure DoEndColumnResize( columnindex, columnwidth: Integer );
virtual;
Procedure DoColumnResize( columnindex, columnwidth: Integer );
virtual;
Procedure WMNotify( Var msg: TWMNotify ); message WM_NOTIFY;
Function FindColumnIndex( pHeader: pNMHdr ): Integer;
function FindColumnWidth(pHeader: pNMHdr): Integer;
Procedure CreateWnd; override;
published
property OnBeginColumnResize: TLVColumnResizeEvent
read FBeginColumnResizeEvent
write FBeginColumnResizeEvent;
property OnEndColumnResize: TLVColumnResizeEvent
read FEndColumnResizeEvent
write FEndColumnResizeEvent;
property OnColumnResize: TLVColumnResizeEvent
read FColumnResizeEvent
write FColumnResizeEvent;
end;
procedure Register;
implementation
uses CommCtrl;
procedure Register;
begin
RegisterComponents('PBGoodies', [TPBExListview]);
end;
procedure TPBExListview.DoBeginColumnResize( columnindex, columnwidth:
Integer );
begin
If Assigned( FBeginColumnResizeEvent ) Then
FBeginColumnResizeEvent( self, columnindex, columnwidth );
end;
procedure TPBExListview.DoEndColumnResize( columnindex, columnwidth:
Integer );
begin
If Assigned( FEndColumnResizeEvent ) Then
FEndColumnResizeEvent( self, columnindex, columnwidth );
end;
procedure TPBExListview.DoColumnResize( columnindex, columnwidth:
Integer );
begin
If Assigned( FColumnResizeEvent ) Then
FColumnResizeEvent( self, columnindex, columnwidth );
end;
DoEndColumnResize( FindColumnIndex( msg.NMHdr ),
FindColumnWidth( msg.NMHdr ));
HDN_BEGINTRACK:
DoBeginColumnResize( FindColumnIndex( msg.NMHdr ),
FindColumnWidth( msg.NMHdr ));
HDN_TRACK:
DoColumnResize( FindColumnIndex( msg.NMHdr ),
FindColumnWidth( msg.NMHdr ));
End;
end;
Procedure TPBExListview.CreateWnd;
var
wnd: HWND;
begin
inherited;
wnd:= GetWindow( handle, GW_CHILD );
SetWindowLong( wnd, GWL_STYLE,
GetWindowLong( wnd, GWL_STYLE ) and not HDS_FULLDRAG
);
end;
function TPBExListview.FindColumnWidth(pHeader: pNMHdr): Integer;
begin
Result := -1;
If Assigned( PHDNotify( pHeader )^.pItem ) AND
((PHDNotify( pHeader )^.pItem^.mask and HDI_WIDTH) <> 0 )
Then
Result := PHDNotify( pHeader )^.pItem^.cxy;