【问题标题】:TListView: Last Column's caption lost after Columns.Delete(index)TListView:在 Columns.Delete(index) 之后丢失了最后一列的标题
【发布时间】:2012-09-12 20:42:14
【问题描述】:

我想我在 TListView 中发现了一个潜在的错误。

重现步骤: 创建一个新的 VCL Forms 应用程序,添加一个 TListView,将其 ViewStyle 设置为 vsReports。 添加两个按钮

按钮1:

procedure TForm1.Button1Click(Sender: TObject);
var
  lCol: TListColumn;
begin
  lcol :=   ListView1.Columns.Add;
  lcol.Caption := 'name';
  lcol :=   ListView1.Columns.Add;
  lcol.Caption := 'name2';
  lcol :=   ListView1.Columns.Add;
  lcol.Caption := 'name3';
end;

按钮2:

procedure TForm1.Button2Click(Sender: TObject);
begin
  ListView1.Columns.Delete(1);
end;

结果: 该列被删除,但最后一列的标题丢失。当添加更多列并删除其他列之间的列(或删除第一列)时,也会发生这种情况。最后一列的标题始终为空。

我正在使用 XE3。有什么我错过的吗?

谢谢

编辑: QC link

potential duplicate

【问题讨论】:

  • 无法在 Delphi 2009 中重现。所以我认为您最好在 QC 中为此创建一个问题。
  • 好的,也许这是 XE3 的功能 :) 谢谢
  • 最小化/恢复没有帮助。我试图在 XE 和 XE2 中重现,但这似乎只是 XE3。将在 QC 上打开错误报告
  • 我可以在这里使用 Delphi XE2 重现它,如果您尝试调整缺少的标题列的大小,则会触发 AV。
  • 请有人对此进行 QC 并编辑问题以包含指向 QC 报告的链接。

标签: delphi vcl tlistview columnheader


【解决方案1】:

除了问题中报告的内容之外,还有更多内容(更多内容在最后)。

这与您之前的@​​987654321@ 有关。该问题涉及列表视图控件在添加列后移动列时丢失列和项/子项之间的映射。我为comctrls.pas 提出了一个可能的修复方案,其中涉及在移动列时保留FOrderTags 列。每当移动列时,VCL 都会从头开始构建“FOrderTag” - 不考虑列的当前位置。

然后发生的事情是,您提交bug report,提交可能的修复作为解决方法,然后它会完全按原样签入。现在您发现的问题是,当我们保留每列的FOrderTag,然后从中间删除一列时,我们会创建一个洞——它们不再是连续的(比如我们有各自顺序的 0、1 和 2 列标签,删除第 1 列,现在我们有 2 列,订单标签为 0 和 2)。显然原生控件不喜欢这样。

再次修改 VCL,我们可以在移除列时移除任何可能的孔。当您将 comment 中提到的缺少标题的列调整大小/移动到问题时,下面似乎会处理缺少的标题和 AV。

destructor TListColumn.Destroy;
var
  Columns: TListColumns;
  i: Integer; //+
begin
  Columns := TListColumns(Collection);
  if TListColumns(Collection).Owner.HandleAllocated then
    ListView_DeleteColumn(TListColumns(Collection).Owner.Handle, Index);
//{+
  for i := 0 to Columns.Count - 1 do
    if Columns[i].FOrderTag > FOrderTag then
      Dec(Columns[i].FOrderTag);
//}
  inherited Destroy;
  Columns.UpdateCols;
end;


现在,如果我们回到问题中未报告的内容,如果您插入了一些子项,您会注意到它们保留了它们的位置,IOW 列和子项之间的映射丢失了。您的观点可能与我的观点不同,但我认为已删除列的子项应该会丢失。不幸的是,我无法找到实现这一目标的方法。


编辑:我想不出任何可以在 VCL 中轻松集成/修复的东西。没有什么能阻止您删除第一个插入的列。这一项对应items,如果我们删除items,所有子item也会被取出。 VCL 中的当前实现是,实际上,当您删除一列时,不会删除任何项目数据。您可以通过在删除一列后添加一列来验证这一点,子项将神奇地出现在新列下。

无论如何,我可以建议您手动删除已删除列的子项。以下是删除列及其相应子项的实用程序示例:

procedure ListViewDeleteColumn(ListView: TListView; Col: Integer);
var
  i: Integer;
  ColumnOrder: array of Integer;
begin
  SetLength(ColumnOrder, ListView.Columns.Count);
  ListView_GetColumnOrderArray(
        ListView.Handle, ListView.Columns.Count, PInteger(ColumnOrder));
  Assert(ColumnOrder[Col] <> 0, 'column with items cannot be removed');

  for i := 0 to ListView.Items.Count - 1 do
    if Assigned(ListView.Items[i].SubItems) and
        (ListView.Items[i].SubItems.Count >= Col) then
    ListView.Items[i].SubItems.Delete(ColumnOrder[Col] - 1);

  ListView.Columns.Delete(Col);
end;

如果您决定删除第一列,请决定您将如何处理项目/子项目并重建它们。

【讨论】:

  • 再次感谢 sertac :) 所以这次我可能不会向 QC 建议这个解决方法,让他们做他们的工作。也许他们也会修复子项目。你是对的,连接到该列的子项也应该被删除
  • @torno - 供应商如何处理错误报告由他们自己决定。但就目前而言,这只是一半的工作,所以我认为不值得将其放入报告中。不客气!
【解决方案2】:

如果您使用下面的代码删除最后一列,它可以正常工作:

uses
 CommCtrl;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ListView_DeleteColumn(ListView1.Handle, 2);
end;

【讨论】:

  • 由于某些原因,使用上面的这个宏后 Columns.Count 没有更新。有什么建议如何强制更新?
  • @torno,列存储在TListColumns 集合中(应该从中删除列)。由于您直接使用ListView_DeleteColumn Windows API 宏删除列,因此该集合未更新。使用此解决方法您破坏了集合同步。换句话说,这只是一个糟糕的解决方法。
  • 目标不是删除最后一列。目标是删除最后一列之外的任何内容,并使最后一列正常显示和运行。
  • @TLama:是的,显然是这样。
  • 我想弄清楚如何更新列,但我认为我的答案不是那么好......我可能会删除它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-03
  • 2010-10-12
  • 2011-07-25
  • 1970-01-01
相关资源
最近更新 更多