View Issue Details

IDProjectCategoryView StatusLast Update
0017141LazarusWidgetsetpublic2010-08-10 11:42
ReporterBernd KreussAssigned ToPaul Ishenin 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version0.9.29 (SVN)Product Build 
Target VersionFixed in Version0.9.29 (SVN) 
Summary0017141: on win32 TListView column autosize=True makes inserting extremely slow and it also ignores column header size
Descriptionon win32 TListView column autosize=True makes inserting extremely slow, even with BeginUpdate

Attached is a patch.

The native listview widget on win32 will ignore WM_SETREDRAW=false when it comes to autosizing the columns.

The current implementation will trigger an autosize on every ItemSetText() and although redraw should be deactivated within BeginUpdate/EndUpdate the native listview control will simply ignore this setting when it comes to column autosizing. The result is one can watch all the column headers *slowly* adapting the sizes while the list is filled.

The other problem is the column headers are not included in the autosize calculation.

The attached patch will fix these problems in the folowing way:

* using LVSCW_AUTOSIZE_USEHEADER instead of LVSCW_AUTOSIZE will fix the column header issue.

* ItemSetText() will now not call the autosize everytime anymore when it is called within BeginUpdate/EndUpdate, instead this will then be postponed until EndUpdate.

* EndUpdate will now autosize all autosize=true columns from left to right and since the widget would ignore the WM_SETREDRAW=false and still redraw the list once for every column this is done while the listview is temporarily set to SW_HIDE during this operation. There is no forced redraw between SW_HIDE and SW_SHOW, so it will now be completely flicker-free and lightning fast.

* a new potected function GetUpdateCount() needed to be added to TCustomListView to give the interface class access to the current BeginUpdate/EndUpdate state so it can decide whether to autosize immediately or postpone it.

Attached is the patch and also a simple demo program which will demonstrate the issues on win32, compare its bahavior before and after this patch.
TagsNo tags attached.
Fixed in Revision27046
LazTarget-
WidgetsetWin32/Win64
Attached Files
  • listview_autosize.diff (3,829 bytes)
    Index: lcl/comctrls.pp
    ===================================================================
    --- lcl/comctrls.pp	(Revision 27042)
    +++ lcl/comctrls.pp	(Arbeitskopie)
    @@ -1107,7 +1107,8 @@
         function CustomDrawItem(AItem: TListItem; AState: TCustomDrawState; AStage: TCustomDrawStage): Boolean; virtual;                       //
         function CustomDrawSubItem(AItem: TListItem; ASubItem: Integer; AState: TCustomDrawState; AStage: TCustomDrawStage): Boolean; virtual; //
         function IntfCustomDraw(ATarget: TCustomDrawTarget; AStage: TCustomDrawStage; AItem, ASubItem: Integer; AState: TCustomDrawState; const ARect: PRect): TCustomDrawResult;
    -    
    +    function GetUpdateCount: Integer;
    +
         procedure DoGetOwnerData(Item: TListItem); virtual;
       protected
         property AllocBy: Integer read FAllocBy write SetAllocBy default 0;
    Index: lcl/include/customlistview.inc
    ===================================================================
    --- lcl/include/customlistview.inc	(Revision 27042)
    +++ lcl/include/customlistview.inc	(Arbeitskopie)
    @@ -521,6 +521,11 @@
       then Result := [cdrSkipDefault];
     end;
     
    +function TCustomListView.GetUpdateCount: Integer;
    +begin
    +  Result := FUpdateCount;
    +end;
    +
     procedure TCustomListView.DoGetOwnerData(Item: TListItem); 
     begin
       if Assigned(OnData) then OnData(Self, Item);
    Index: lcl/interfaces/win32/win32wscustomlistview.inc
    ===================================================================
    --- lcl/interfaces/win32/win32wscustomlistview.inc	(Revision 27042)
    +++ lcl/interfaces/win32/win32wscustomlistview.inc	(Arbeitskopie)
    @@ -17,7 +17,7 @@
     
     { TWin32WSCustomListView }
     const
    -  AutoSizeWidth = LVSCW_AUTOSIZE{_USEHEADER};
    +  AutoSizeWidth = LVSCW_AUTOSIZE_USEHEADER;
     
     type
       TLVStyleType = (lsStyle, lsInvert, lsExStyle);
    @@ -673,9 +673,13 @@
       {$else}
         ListView_SetItemText(ALV.Handle, AIndex, ASubIndex, PChar(AText));
       {$endif}
    -  if ALV.Column[ASubIndex].AutoSize then begin
    -    ListView_SetColumnWidth(ALV.Handle, ASubIndex, LVSCW_AUTOSIZE);
    -  end;
    +  // autosize is an *exteme* performance bottleneck, even if WM_SETREDRAW
    +  // was set to false it will ignore this and still redraw all columns.
    +  // We will therefore postpone all autosizing until EndUpdate where we do
    +  // it only once per column.
    +
    +  if ALV.Column[ASubIndex].AutoSize and (TCustomListViewAccess(ALV).GetUpdateCount = 0) then
    +    ListView_SetColumnWidth(ALV.Handle, ASubIndex, AutoSizeWidth);
     end;
     
     class procedure TWin32WSCustomListView.ItemShow(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean);
    @@ -731,11 +735,29 @@
     end;
     
     class procedure TWin32WSCustomListView.EndUpdate(const ALV: TCustomListView);
    +var
    +  ColIndex : Integer;
     begin
       if not WSCheckHandleAllocated(ALV, 'EndUpdate')
       then Exit;
     
    +  // we have skipped all column resizing in ItemSetText()
    +  // for performance reasons, so now we need to do it here.
    +  //
    +  // A further significant perfomance boost and reduced flickering
    +  // can be achieved by setting the widget to invisible during the
    +  // following operation (it ignores the state of WM_SETREDRAW for
    +  // column resizing, but this way we we can really enforce it).
    +  // ShowWindow() itself does not force an immediate redraw,
    +  // so it won't flicker at all.
    +  ShowWindow(ALV.Handle, SW_HIDE);
    +  for ColIndex := 0 to TCustomListViewAccess(ALV).Columns.Count - 1 do
    +    if ALV.Column[ColIndex].AutoSize then
    +      ListView_SetColumnWidth(ALV.Handle, ColIndex, AutoSizeWidth);
    +
       SendMessage(ALV.Handle,WM_SETREDRAW,WPARAM(True),0);
    +  if ALV.Visible then
    +    ShowWindow(ALV.Handle, SW_SHOW);
     end;
     
     class function TWin32WSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect;
    
    listview_autosize.diff (3,829 bytes)
  • listview_autosize_test.zip (2,885 bytes)

Activities

2010-08-09 23:30

 

listview_autosize.diff (3,829 bytes)
Index: lcl/comctrls.pp
===================================================================
--- lcl/comctrls.pp	(Revision 27042)
+++ lcl/comctrls.pp	(Arbeitskopie)
@@ -1107,7 +1107,8 @@
     function CustomDrawItem(AItem: TListItem; AState: TCustomDrawState; AStage: TCustomDrawStage): Boolean; virtual;                       //
     function CustomDrawSubItem(AItem: TListItem; ASubItem: Integer; AState: TCustomDrawState; AStage: TCustomDrawStage): Boolean; virtual; //
     function IntfCustomDraw(ATarget: TCustomDrawTarget; AStage: TCustomDrawStage; AItem, ASubItem: Integer; AState: TCustomDrawState; const ARect: PRect): TCustomDrawResult;
-    
+    function GetUpdateCount: Integer;
+
     procedure DoGetOwnerData(Item: TListItem); virtual;
   protected
     property AllocBy: Integer read FAllocBy write SetAllocBy default 0;
Index: lcl/include/customlistview.inc
===================================================================
--- lcl/include/customlistview.inc	(Revision 27042)
+++ lcl/include/customlistview.inc	(Arbeitskopie)
@@ -521,6 +521,11 @@
   then Result := [cdrSkipDefault];
 end;
 
+function TCustomListView.GetUpdateCount: Integer;
+begin
+  Result := FUpdateCount;
+end;
+
 procedure TCustomListView.DoGetOwnerData(Item: TListItem); 
 begin
   if Assigned(OnData) then OnData(Self, Item);
Index: lcl/interfaces/win32/win32wscustomlistview.inc
===================================================================
--- lcl/interfaces/win32/win32wscustomlistview.inc	(Revision 27042)
+++ lcl/interfaces/win32/win32wscustomlistview.inc	(Arbeitskopie)
@@ -17,7 +17,7 @@
 
 { TWin32WSCustomListView }
 const
-  AutoSizeWidth = LVSCW_AUTOSIZE{_USEHEADER};
+  AutoSizeWidth = LVSCW_AUTOSIZE_USEHEADER;
 
 type
   TLVStyleType = (lsStyle, lsInvert, lsExStyle);
@@ -673,9 +673,13 @@
   {$else}
     ListView_SetItemText(ALV.Handle, AIndex, ASubIndex, PChar(AText));
   {$endif}
-  if ALV.Column[ASubIndex].AutoSize then begin
-    ListView_SetColumnWidth(ALV.Handle, ASubIndex, LVSCW_AUTOSIZE);
-  end;
+  // autosize is an *exteme* performance bottleneck, even if WM_SETREDRAW
+  // was set to false it will ignore this and still redraw all columns.
+  // We will therefore postpone all autosizing until EndUpdate where we do
+  // it only once per column.
+
+  if ALV.Column[ASubIndex].AutoSize and (TCustomListViewAccess(ALV).GetUpdateCount = 0) then
+    ListView_SetColumnWidth(ALV.Handle, ASubIndex, AutoSizeWidth);
 end;
 
 class procedure TWin32WSCustomListView.ItemShow(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean);
@@ -731,11 +735,29 @@
 end;
 
 class procedure TWin32WSCustomListView.EndUpdate(const ALV: TCustomListView);
+var
+  ColIndex : Integer;
 begin
   if not WSCheckHandleAllocated(ALV, 'EndUpdate')
   then Exit;
 
+  // we have skipped all column resizing in ItemSetText()
+  // for performance reasons, so now we need to do it here.
+  //
+  // A further significant perfomance boost and reduced flickering
+  // can be achieved by setting the widget to invisible during the
+  // following operation (it ignores the state of WM_SETREDRAW for
+  // column resizing, but this way we we can really enforce it).
+  // ShowWindow() itself does not force an immediate redraw,
+  // so it won't flicker at all.
+  ShowWindow(ALV.Handle, SW_HIDE);
+  for ColIndex := 0 to TCustomListViewAccess(ALV).Columns.Count - 1 do
+    if ALV.Column[ColIndex].AutoSize then
+      ListView_SetColumnWidth(ALV.Handle, ColIndex, AutoSizeWidth);
+
   SendMessage(ALV.Handle,WM_SETREDRAW,WPARAM(True),0);
+  if ALV.Visible then
+    ShowWindow(ALV.Handle, SW_SHOW);
 end;
 
 class function TWin32WSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect;
listview_autosize.diff (3,829 bytes)

2010-08-09 23:35

 

listview_autosize_test.zip (2,885 bytes)

Paul Ishenin

2010-08-10 04:08

manager   ~0040096

Thanks, applied. Please close if ok.

Bernd Kreuss

2010-08-10 11:42

reporter   ~0040101

perfect :-)

Issue History

Date Modified Username Field Change
2010-08-09 23:30 Bernd Kreuss New Issue
2010-08-09 23:30 Bernd Kreuss File Added: listview_autosize.diff
2010-08-09 23:30 Bernd Kreuss Widgetset => Win32/Win64
2010-08-09 23:35 Bernd Kreuss File Added: listview_autosize_test.zip
2010-08-10 04:08 Paul Ishenin Fixed in Revision => 27046
2010-08-10 04:08 Paul Ishenin LazTarget => -
2010-08-10 04:08 Paul Ishenin Status new => resolved
2010-08-10 04:08 Paul Ishenin Fixed in Version => 0.9.29 (SVN)
2010-08-10 04:08 Paul Ishenin Resolution open => fixed
2010-08-10 04:08 Paul Ishenin Assigned To => Paul Ishenin
2010-08-10 04:08 Paul Ishenin Note Added: 0040096
2010-08-10 11:42 Bernd Kreuss Status resolved => closed
2010-08-10 11:42 Bernd Kreuss Note Added: 0040101