2

Windows 主题更改后,TListView 的列内容不正确。我已将其缩小到 CM_RECREATE 消息,即 VCL 重新创建 TListView 的窗口句柄以响应系统主题更改。下面是一些说明问题的屏幕截图。

原始列表视图状态

初始列表视图状态

最后一列已移动到第一个位置。一切都很好。

科尔搬家了

Windows 主题更改后,列位置保留,但内容不再正确

主题变化

目前,我通过简单地在我的自定义 CM_RECREATEWND 处理程序中手动重新创建列来克服这个问题。它是一个错误吗?重新创建列是一个很好的解决方案还是有更好的方法?

我正在使用 Delphi10,但在以前的版本中也观察到了相同的行为。

4

1 回答 1

2

如果有人需要快速修复此错误,我将发布我的解决方法。只需将此单元作为最后使用的单元包含在表格的使用列表中即可。

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.
于 2015-10-12T16:34:38.460 回答