OK, there are also properties VisibleRowCount and VisibleColCount.
So you can do:
StringGrid1.TopRow:=StringGrid1.Row-(StringGrid1.VisibleRowCount div 2);
and selected cell will be in the middle (vertically).
Not quite.
VisibleRowCount could change depending on the different row heights (same with
VisibleColCount and col widths).
Also, instead of IsCellVisible() you can use:
if (Row>=TopRow) and (Row<(TopRow+VisibleRowCount)) then
and you can manage vertical and horizontal shift separately.
Trying to cope with variable widths/heights, I've tried a "crawling" approach:
procedure MakeVisible(AGrid: TStringGrid; ACol, ARow: Integer);
begin
AGrid.BeginUpdate; // Comment it to work properly
try
if ARow < AGrid.TopRow then
AGrid.TopRow := ARow
else
while AGrid.TopRow + AGrid.VisibleRowCount <= ARow do
AGrid.TopRow := AGrid.TopRow + 1;
if ACol < AGrid.LeftCol then
AGrid.LeftCol := ACol
else
while AGrid.LeftCol + AGrid.VisibleColCount <= ACol do
AGrid.LeftCol := AGrid.LeftCol + 1;
finally
AGrid.EndUpdate(True); // Comment it to work properly
end;
end;
But it doesn't work well when enclosed in
BeginUpdate/
EndUpdate - seems that
VisibleXXXCount doesn't update properly. Commenting them out makes it plausible, but then you can visually see how the grid gradually gets there.
I finally came to the following solution:
procedure MakeVisible(AGrid: TStringGrid; ACol, ARow: Integer);
var
L: Integer;
begin
AGrid.BeginUpdate;
try
if ARow < AGrid.TopRow then
AGrid.TopRow := ARow // towards NW, N, NE
else if AGrid.TopRow + AGrid.VisibleRowCount < ARow then
begin
if AGrid.FixedRows > 0
then L := AGrid.CellRect(0, Pred(AGrid.FixedRows)).Bottom
else L := 0;
Inc(L, AGrid.RowHeights[ARow]);
while ARow > AGrid.FixedRows do
begin
Inc(L, AGrid.RowHeights[Pred(ARow)]);
if L > AGrid.ClientHeight
then Break
else Dec(ARow)
end;
AGrid.TopRow := ARow; // towards SW, S, SE
end;
if ACol < AGrid.LeftCol then
AGrid.LeftCol := ACol // towards NW, W, SW
else if AGrid.LeftCol + AGrid.VisibleColCount < ACol then
begin
if AGrid.FixedCols > 0
then L := AGrid.CellRect(Pred(AGrid.FixedCols), 0).Right
else L := 0;
Inc(L, AGrid.ColWidths[ACol]);
while ACol > AGrid.FixedCols do
begin
Inc(L, AGrid.ColWidths[Pred(ACol)]);
if L > AGrid.ClientWidth
then Break
else Dec(ACol)
end;
AGrid.LeftCol := ACol; // towards NE, E, SE
end;
finally
AGrid.EndUpdate(True);
end;
end;
Which scans backward to find how many cols/rows can fit into the scrollable area. It works fairly well, though it is not ideal, since it cannot set the topleft origin of the scrollable area in the middle of a cell - something that can be observed when you have columns wider than the grid itself.