function DBGridVisibleDataRows(AGrid: TDBGrid): Integer;
var
LTemTitulo : Boolean;
LLinhaBase : Integer;
r0, r1 : TRect;
rowH, topData,
visH : Integer;
begin
if (AGrid = nil) or (AGrid.ClientHeight <= 0) then
Exit(0);
LTemTitulo := dgTitles in AGrid.Options;
LLinhaBase := Ord(LTemTitulo); // 0 sem títulos, 1 com títulos
// mede altura real de uma linha usando dois retângulos consecutivos
r0 := AGrid.CellRect(0, LLinhaBase);
r1 := AGrid.CellRect(0, LLinhaBase + 1);
rowH := r1.Top - r0.Top;
// fallback se ainda não deu para medir (grid vazio/não pintado)
if rowH <= 0 then
rowH := AGrid.DefaultRowHeight + 1; // não usa GridLineWidth (protected)
// topo da área de dados
if r0.Top > 0 then
topData := r0.Top
else if LTemTitulo then
topData := AGrid.DefaultRowHeight + 1
else
topData := 0;
visH := AGrid.ClientHeight - topData;
if (rowH <= 0) or (visH <= 0) then
Exit(0);
Result := Max(0, visH div rowH);
end;