This article on delphi.about.com shows one way: http://delphi.about.com/od/delphitips2008/qt/dbgrid_row_pos.htm
However, if you consider the following scenario, you’ll notice that one is not working right:
– initial recordset has 10 records
– position on the 9th
– delete first 7 records
– cursor should now be on the 2ndΒ row.
Same goes if we add records in front of the current record.
So, how to do this correctly? The first impuls is to do something like:
b := ds.GetBookmark;
try
finally
if (b<>nil) and ds.BookMarkValid(b) then
try
ds.GotoBookMark(b);
finally
ds.FreeBookMark(b);
end;
end;
That looks all nice and dandy but:
– this will always position the selected row in the center of the grid. It’s pretty annoying if you do repetitive refreshing (like in a timer)
– if the current record is deleted … nothing happens. Of course if you happen to reposition the cursor (like using locate) in the code, then you will have some random re-positionings. Again, not nice.
So lets start with problem no 2.
RecNo := ds.RecNo;
b := ds.GetBookmark;
try
finally
if (b<>nil) and ds.BookMarkValid(b) then
try
ds.GotoBookMark(b);
finally
ds.FreeBookMark(b);
end
else if (recno<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;
Ok, so this solves the case when the current record is deleted. Still we have to fix the centering of the GotoBookMark.
Now, upon inspection of that function, we see that it actually does a Resync and passes rmCenter to it, which is our problem. So let’s simulate the GotobookMark without centering:
THackDataSet=class(TDataSet) // a nice "hack" so we can access protected members
end;
RecNo := ds.RecNo;
b := ds.GetBookmark;
try
finally
if (b<>nil) and ds.BookMarkValid(b) then
try
// Β Β Β Β ds.GotoBookMark(b);
ds.CheckBrowseMode;
THackDataSet(ds).DoBeforeScroll;
THackDataSet(ds).InternalGotoBookmark(b);
ds.Resync([rmExact{, rmCenter}]);
THackDataSet(ds).DoAfterScroll;
finally
ds.FreeBookMark(b);
end
else if (recn<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;
Ok, now this almost solves the problem. Now, the row is repositioned to the last in the grid. Still not what we want. So what we can still do here is force the resync to take the active record the right one and not the one set by the InternalGotoBookmark.
THackDataSet=class(TDataSet) // a nice "hack" so we can access protected members
end;
THackDBGrid=class(TDBGrid)
end;
Row := THackDBGrid(grid).Row;// or THackDataSet(ds).ActiveRecord
RecNo := ds.RecNo;
b := ds.GetBookmark;
try
finally
if (b<>nil) and ds.BookMarkValid(b) then
try
// Β Β Β Β ds.GotoBookMark(b);
ds.CheckBrowseMode;
THackDataSet(ds).DoBeforeScroll;
THackDataSet(ds).InternalGotoBookmark(b);
if THackDataSet(ds).ActiveRecord <> Row - 1 then
THackDataSet(ds).MoveBy(Row - THackDataSet(ds).ActiveRecord - 1);
ds.Resync([rmExact{, rmCenter}]);
THackDataSet(ds).DoAfterScroll;
finally
ds.FreeBookMark(b);
end
else if (recno<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;
This is almost perfect. There is still the same issue of positioning on the first/last record when the current record is deleted. I can’t confirm this yet as my test scenarios haven’t gotten that far but I *think* it will happen. And if it does, you can simply apply the same ActiveRecord thing to calculate the correct position for the MoveBy.
I’ll update this post if and when I get to kick this scenario.
http://delphi.about.com/od/delphitips2008/qt/dbgrid_row_pos.htm
Related posts