Tuesday, August 29, 2006

TListView troubles

I have been trying to write some owner-draw procedures for a TListView in Delphi. Specifically, I need to draw some small bitmaps in the vsReport style. I thought this would be easy. So I used the OnCustomDrawSubItem event and all seemed fine until I started changing the width of the columns during run-time. The problem was that ‘column.width’ was not being updated during the dragging and the result was a mess. After looking into the source code of the ComCtrls unit I decided to write a custom control that would update the column widths properly. Again, I thought this would be easy. I spent almost all day trying to figure out why things were not working. Finally I discovered this blog entry: http://thomasfreudenberg.com/blog/comments/352.aspx.
It seems that Microsoft has changed the notification for TListView and HDN_TRACK may not work. So, if you are having the same problems, here is my complete solution. It emulates a OnColumnTrack event (similar to the OnSectionTrack event of THeaderControl) and uses both HDN_TRACK and HDN_ITEMCHANGING to detect column changes.
This goes in the interface section of your unit:


type
 TdhColumnTrackEvent = procedure(Sender: TCustomListview; Column: TListColumn; Width: Integer; State: TSectionTrackState) of object;

TdhListView = class(TListview)
 private
  FColumnTrackEvent: TdhColumnTrackEvent;
 protected
  procedure WMNotify(var Msg: TWMNotify); message WM_NOTIFY;
 published
  property OnColumnTrack: TdhColumnTrackEvent read FColumnTrackEvent write FColumnTrackEvent;
end;


This goes in the implementation section:

procedure TdhListView.WMNotify(var Msg: TWMNotify);
var
 TrackState: TSectionTrackState;
 aColumn: TListColumn;
begin
 inherited;
 with Msg do
 case NMHdr^.code of
  HDN_BEGINTRACK, HDN_ENDTRACK, HDN_TRACK, HDN_ITEMCHANGING:
   begin
    case NMHdr^.code of
     HDN_BEGINTRACK: TrackState := tsTrackBegin;
     HDN_ENDTRACK: TrackState := tsTrackEnd;
    else
     TrackState := tsTrackMove;
    end;
   with PHDNotify(Pointer(NMHdr))^, PItem^ do
    if ((Mask and HDI_WIDTH) <> 0) then
     begin
      aColumn := Columns[Item];
       if Assigned(FColumnTrackEvent) then FColumnTrackEvent(self, aColumn, cxy, TrackState);
     end;
   end;
  end;
end;

1 comment:

  1. Anonymous10:18 PM

    Dude, all you need to do is remove the HDS_FULLDRAG from the list's header control's style (1 line of code) instead of this solution.


    SUMMARY from the relevan Microsoft article:
    Starting with version 4.70 of ComCtl32.dll, the header control of a list view control in report view (LVS_REPORT) automatically receives the HDS_FULLDRAG style. When this style is set, the parent of a list view control receives HDN_ITEMCHANGING notifications, rather than HDN_TRACK notifications, when the column divider of the header control is dragged. To receive HDN_TRACK notifications, the header control of the list view control must not have the HDS_FULLDRAG style set. Note that the HDS_FULLDRAG style is ignored in versions of ComCtl32.dll prior to 4.70.

    ReplyDelete