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.
- Put a TAdvStringGrid, a TButton and a TCheckbox on a form.
- Add the code fragments below. Set the form's OnCreate event handler to FormCreate and the button's OnClick event handler to Button1Click.
- 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.
- 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.