TAdvStringGrid hidden rows cache causes exceptions

Between version 6.0.3.0 and version 7.7.3.0 of TAdvStringGrid the function RemapRowInv has been optimized to use a cache for hidden rows. Unfortunately, this cache is not always reset or rebuilt if it's dependent data structure FGridItems is changed which results in wrong values to be returned by RemapRowInv. In our application this causes some invalid pointer exceptions. As a workaround, we reverted to the code of version 6.0.3.0 for function RemapRowInv in version 7.7.3.0.

Do you have some sample source project with which this can be reproduced as we'd like to investigate under what exact circumstances this cache could be out of sync.

  1. Put a TAdvStringGrid, a TButton and a TCheckbox on a form.
  2. Add the code fragments below. Set the form's OnCreate event handler to FormCreate and the button's OnClick event handler to Button1Click.
  3. Start the program. Leave the checkbox unchecked and remove the nodes by clicking the button 5 times. You'll see that the some of the lines with a number between 1 and 30 will disappear which is not correct.
  4. Restart the program. Check the checkbox and remove the nodes again. You'll see that all lines with a number between 1 and 30 remain intact, if the hidden rows cache is reset properly.




interface
...

type
  TMyAdvStringGrid = class helper for TAdvStringGrid
    procedure ResetHiddenRowsCache;
  end;
...

implementation

uses
  Math;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

  procedure Local_RemoveNodeAndChildren(piRow : Integer);
  var
    liRow : Integer;
  begin
    if AdvStringGrid1.IsNode(piRow) then
    begin
      if AdvStringGrid1.GetNodeState(piRow) then
      AdvStringGrid1.ExpandNode(AdvStringGrid1.RealRowIndex(piRow));

      liRow := piRow + 1;
      while (liRow < AdvStringGrid1.RowCount) and (AdvStringGrid1.GetParentRow(liRow) = piRow) do
        AdvStringGrid1.RemoveChildRow(AdvStringGrid1.RealRowIndex(liRow));

      if CheckBox1.Checked then
        AdvStringGrid1.ResetHiddenRowsCache;

      AdvStringGrid1.RemoveNode(AdvStringGrid1.RealRowIndex(piRow));
    end;
  end;

var
  liRow : Integer;
begin
  liRow := AdvStringGrid1.Row;

  Local_RemoveNodeAndChildren(liRow);
  // set to next node
  while (liRow < AdvStringGrid1.RowCount) and
        not AdvStringGrid1.IsNode(liRow) do
    Inc(liRow);
  AdvStringGrid1.Row := Min(liRow, AdvStringGrid1.RowCount - 1);
end;

procedure TForm1.FormCreate(Sender: TObject);

  procedure Local_AddChildRow(piRow     : Integer;
                              piChildNo : Integer);
  begin
    AdvStringGrid1.InsertChildRow(AdvStringGrid1.RealRowIndex(piRow), piChildNo);
    AdvStringGrid1.Cells[2, piRow + piChildNo] := 'Child ' + IntToStr(piRow) + '.' + IntToStr(piChildNo);
  end;

  procedure Local_AddNodeAndChildren(piRow        : Integer;
                                     piChildCount : Integer);
  var
    liChild    : Integer;
    lbContract : Boolean;
  begin
    // add node and expand
    lbContract := True;
    if not AdvStringGrid1.IsNode(piRow) then
      AdvStringGrid1.AddNode(AdvStringGrid1.RealRowIndex(piRow),1)
    else
    begin
      if AdvStringGrid1.GetNodeState(piRow) then
      begin
        AdvStringGrid1.ExpandNode(AdvStringGrid1.RealRowIndex(piRow));
      end
      else
        lbContract := False;
    end;

    // add children
    for liChild := 1 to piChildCount do
      Local_AddChildRow(piRow, liChild);

    // contract node
    if AdvStringGrid1.IsNode(piRow) and lbContract then
      AdvStringGrid1.ContractNode(AdvStringGrid1.RealRowIndex(piRow));
  end;

var
  liRow : Integer;
begin
  AdvStringGrid1.FixedCols := 0;
  AdvStringGrid1.RowCount  := 31;

  Button1.Caption := 'Remove node';
  CheckBox1.Width := 250;
  CheckBox1.Caption := 'Reset hidden rows cache after removing node';

  for liRow  := 1 to AdvStringGrid1.RowCount -1 do
    AdvStringGrid1.Cells[1,liRow] := IntToStr(liRow);

  Local_AddNodeAndChildren( 1, 2);
  Local_AddNodeAndChildren( 3, 2);
  Local_AddNodeAndChildren(11, 2);
  Local_AddNodeAndChildren(13, 2);
  Local_AddNodeAndChildren(26, 2);

  AdvStringGrid1.Col       := 1;
end;

procedure TMyAdvStringGrid.ResetHiddenRowsCache;
begin
  DestroyHiddenRowLookup;
end;


Thanks for these details.We're investigating a solution for the issue.