在Delphi中,在THeadercontrol上调用FlipChildren会出​​现错误

我刚刚遇到Delphi中的一个行为,这对我来说似乎是个错误.

在Delphi中,只需在表单上删除一个THeaderControl,并为其分配至少一个部分.如果在当前表单上调用FlipChildren(true),则会出现“列表索引超出范围”错误.看起来TCustomHeaderControl的FlipChildren过程中存在问题.

由于在各种版本的Delphi(我已经尝试过Delphi 6和Delphi 2010)中可以重现相同的行为,我有点不愿意将这个版本归类为bug.其他人之前遇到过这个问题?

最佳答案 这绝对是一个错误.我希望代码在Delphi 1中起作用,但是THeaderSections的实现以破坏它的方式发生了变化.从那时起,您似乎是第一个执行代码的人!

这是代码:

procedure TCustomHeaderControl.FlipChildren(AllLevels: Boolean);
var
  Loop, FirstWidth, LastWidth: Integer;
  ASectionsList: THeaderSections;
begin
  if HandleAllocated and
     (Sections.Count > 0) then
  begin
    { Get the true width of the last section }
    LastWidth := ClientWidth;
    FirstWidth := Sections[0].Width;
    for Loop := 0 to Sections.Count - 2 do Dec(LastWidth, Sections[Loop].Width);
    { Flip 'em }
    ASectionsList := THeaderSections.Create(Self);
    try
      for Loop := 0 to Sections.Count - 1 do with ASectionsList.Add do
        Assign(Self.Sections[Loop]);
      for Loop := 0 to Sections.Count - 1 do
        Sections[Loop].Assign(ASectionsList[Sections.Count - Loop - 1]);
    finally
      ASectionsList.Free;
    end;
    { Set the width of the last Section }
    if Sections.Count > 1 then
    begin
      Sections[Sections.Count-1].Width := FirstWidth;
      Sections[0].Width := LastWidth;
    end;
    UpdateSections;
  end;
end;

我们的想法是构建一个临时的标题部分列表,从真实的部分中分配属性.然后以相反的顺序遍历临时列表,分配回标题部分的真实列表.但它不起作用.

整个代码都是假的,因为实际上只涉及一个集合.与控件关联的集合. THeaderSections的设计假定标题控件和THeaderSections对象之间存在一对一的关系.可以很容易地观察到,ASectionsList.Add实际上将项添加到SectionsList!

所以,当这段代码完成运行时

for Loop := 0 to Sections.Count - 1 do with ASectionsList.Add do
  Assign(Self.Sections[Loop]);

您将观察到Sections.Count已加倍,并且ASectionsList.Count仍为零.那么当我们继续运行时

for Loop := 0 to Sections.Count - 1 do
  Sections[Loop].Assign(ASectionsList[Sections.Count - Loop - 1]);

ASectionsList [Sections.Count – Loop – 1]的访问超出范围.

代码非常糟糕.我对此感到震惊.所需要的只是一个包含宽度的简单整数数组.这是它应该如何看,用插入器实现:

type
  THeaderControl = class(Vcl.ComCtrls.THeaderControl)
  public
    procedure FlipChildren(AllLevels: Boolean); override;
  end;

procedure THeaderControl.FlipChildren(AllLevels: Boolean);
var
  Index, Count: Integer;
  Widths: TArray<Integer>;
begin
  Count := Sections.Count;
  if Count>1 then
  begin
    SetLength(Widths, Count);
    for Index := 0 to Count-2 do
      Widths[Index] := Sections[Index].Width;
    Widths[Count-1] := ClientWidth;
    for Index := 0 to Count-2 do
      dec(Widths[Count-1], Widths[Index]);
    Sections.BeginUpdate;
    try
      for Index := 0 to Sections.Count-1 do
        Sections[Index].Width := Widths[Count-Index-1];
    finally
      Sections.EndUpdate;
    end;
  end;
end;

我建议您提交质量控制报告.

点赞