Reputation: 1061
TListView's column contents become incorrect after windows theme change. I've narrowed it down to CM_RECREATE message, that's when VCL recreates TListView's window handle in response to system theme change. Below are some screenshots illustrating the problem.
Original list view state
Last column has been moved moved to the first position. Everything is fine.
After Windows theme was changed, the columns positions are preserved, however, the contents are no longer correct.
Currently I overcome the issue by simply recreating the columns manually in my custom CM_RECREATEWND handler. Is it a bug? It it a good solution to recreate columns or is there a better way?
I'm using Delphi10 but the same behavior was observed in the previous versions as well.
Upvotes: 2
Views: 257
Reputation: 1061
I'll post my workaround in case anyone needs a quick fix for this bug. Just include this unit as a last used unit in a Form's uses list.
unit LVFix;
interface
uses
Winapi.Windows, Winapi.Messages, System.Classes, System.UITypes,
Vcl.Controls, Vcl.ComCtrls;
type
TListView = class(Vcl.ComCtrls.TListView)
strict private
type
TColumnRec = record
Alignment: TAlignment;
AutoSize: Boolean;
Caption: String;
ImageIndex: TImageIndex;
MaxWidth, MinWidth, Width: TWidth;
Tag: Integer;
ID: Integer;
end;
var
FSavedCols: TArray<TColumnRec>;
FSavedColOrder: TArray<Integer>;
private
procedure SaveColumnState;
procedure RestoreColumnState;
protected
procedure CMRecreate(var M: TMessage); message CM_RECREATEWND;
end;
implementation
uses
Winapi.CommCtrl;
{ TListView }
procedure TListView.CMRecreate(var M: TMessage);
begin
SaveColumnState;
inherited;
RestoreColumnState;
end;
procedure TListView.RestoreColumnState;
var
I: Integer;
begin
Items.BeginUpdate; //lock to prevent unnecessary events firing
try
//recreate columns
Columns.Clear;
for I := 0 to High(FSavedCols) do
begin
with Columns.Add do
begin
Alignment := FSavedCols[I].Alignment;
AutoSize := FSavedCols[I].AutoSize;
Caption := FSavedCols[I].Caption;
ImageIndex := FSavedCols[I].ImageIndex;
MinWidth := FSavedCols[I].MinWidth;
MaxWidth := FSavedCols[I].MaxWidth;
Width := FSavedCols[I].Width;
Tag := FSavedCols[I].Tag;
end;
end;
//restore column order
if Length(FSavedColOrder) <> 0 then
ListView_SetColumnOrderArray(Handle, Columns.Count, PInteger(FSavedColOrder));
finally
Items.EndUpdate;
end;
end;
procedure TListView.SaveColumnState;
var
R: LongBool;
I: Integer;
J: Integer;
T: TColumnRec;
begin
//save column order
SetLength(FSavedColOrder, Columns.Count);
R := ListView_GetColumnOrderArray(Handle, Columns.Count, PInteger(FSavedColOrder));
if not R then
SetLength(FSavedColOrder, 0);
//save original columns in original order
SetLength(FSavedCols, Columns.Count);
for I := 0 to Columns.Count - 1 do
begin
FSavedCols[I].Alignment := Columns[I].Alignment;
FSavedCols[I].AutoSize := Columns[I].AutoSize;
FSavedCols[I].Caption := Columns[I].Caption;
FSavedCols[I].ImageIndex := Columns[I].ImageIndex;
FSavedCols[I].MinWidth := Columns[I].MinWidth;
FSavedCols[I].MaxWidth := Columns[I].MaxWidth;
FSavedCols[I].Width := Columns[I].Width;
FSavedCols[I].Tag := Columns[I].Tag;
FSavedCols[I].ID := Columns[I].ID;
end;
for I := 0 to High(FSavedCols) - 1 do
for J := I + 1 to High(FSavedCols) do
if FSavedCols[J].ID < FSavedCols[I].ID then
begin
T := FSavedCols[I];
FSavedCols[I] := FSavedCols[J];
FSavedCols[J] := T;
end;
end;
end.
Upvotes: 2