DBGrid的当前行号和显示行数是受保护的属性,
所以你需要在代码中添加一个“classcracker”类型声明,如下所示:
type
TMyDBGrid = Class(TDBGrid);
function TForm1.GetGridRow: Integer;
begin
Result := TmyDBGrid(DBGrid1).Row;
end;
function TForm1.GridRowCount : Integer;
begin
Result := TmyDBGrid(DBGrid1).RowCount;
end;
完成此操作后,在表单上放置一个 TEdit 和 TButton 以输入小于当前行号的新网格行号。然后尝试以下例程:
procedure TForm1.SetGridRow(NewRow : Integer);
var
GridRows,
OldRow,
MoveDataSetBy,
MovedBy : Integer;
DataSet : TDataSet;
Possible : Boolean;
ScrollUp : Boolean;
begin
OldRow := GetGridRow;
if NewRow = OldRow then
Exit;
ScrollUp := NewRow < OldRow;
DataSet := dBGrid1.DataSource.DataSet;
GridRows := TmyDBGrid(DBGrid1).RowCount;
{ TODO : Test the case where the DataSet doesn't have enough rows to fill the grid}
{ TODO : Check why grid reports one more row than it displays.
Meanwhile ... }
GridRows := GridRows - 1;
// First check whether the NewRow value is sensible
Possible := (NewRow >= 1) and (NewRow <= GridRows);
if not Possible then exit;
try
if ScrollUp then begin
// First scroll the dataset forwards enough to bring
// a number of new records into view
MoveDataSetBy := GridRows - NewRow;
MovedBy := DataSet.MoveBy(MoveDataSetBy);
Shortfall := MoveDataSetBy - MovedBy;
if Shortfall = 0 then begin
// Now scroll the dataset backwards to get back
// to the record we were on
MoveDataSetBy := -GridRows + NewRow;
MovedBy := DataSet.MoveBy(MoveDataSetBy);
end
else
MovedBy := DataSet.MoveBy(-MovedBy);
end
else begin
MoveDataSetBy := -(NewRow - 1);
MovedBy := DataSet.MoveBy(MoveDataSetBy);
// We need to know if the DS cursor was able to move far enough
// back as we've asked or was prevented by reaching BOF
Shortfall := MoveDataSetBy - MovedBy;
if Shortfall = 0 then begin
// The DS cursor succeeded on moving the requested distance
MoveDataSetBy := NewRow - 1;
MovedBy := DataSet.MoveBy(MoveDataSetBy);
end
else
// it failed, so we need to return to the record we started on
// but this won't necessarily return us the same grid row number
MovedBy := DataSet.MoveBy(-MovedBy);
finally
DBGrid1.Invalidate;
end;
我之前的建议是通过“Tma DBGrid(DBGrid).Row := NewRow;”直接分配给网格行是基于错误的记忆,因为事实上这似乎没有什么用处。
由于我们不依赖于有意义的 RecNo,因此“if ScrollUp”之后的算法变得复杂。这涉及到检查数据集光标是否可以沿该方向移动足够的量opposite我们想要将网格行移入,以相对于网格中的行滚动 DS 光标,而不点击 EOF 或 BOF - 如果发生其中任何一种,我们只需将 DS 光标移回原来的位置并放弃尝试滚动网格。
对于ScrollUp,逻辑是:
- 首先将数据集光标移动到网格中的最后一行
- 然后根据新旧 Row 值之间的差异将其向前移动一些。
- 然后将其向后移动,移动量等于网格中的行数减去新行值。
如果一切成功,当前行将移动到 NewRow 值所请求的网格位置。
当然,该代码结合了前两个步骤。起初,我认为这段代码是无意义的,因为用于 DataSet.MoveBy()s 的值的代数和为零。实际上,
这不是废话,只是有点违反直觉。当然,距离加起来为零,因为我们想回到我们曾经的记录;执行 DataSet.MoveBy() 的目的就是要松开网格对当前记录的控制,然后返回到它。顺便说一句,这就是为什么在移出当前记录然后返回到它时没有必要执行我通常的操作,即 DataSet.GetBookmark/GotBookmark/FreeBookmark ,并且实际上使用这些会破坏代码的预期效果。
顺便说一句,我使用的是 ClientDataSet,而不是 ZEOS,但这应该没有任何区别。
顺便说一句,本地 DataSet 变量用于访问网格的数据集,而不使用 Delphi 的地狱般的“With ...”构造。
顺便说一句,您对“Rows div 2”的评论提醒了我:我不认为网格告诉数据集,ISTR它是与网格关联的数据链接,它告诉数据集应该为多少记录分配缓冲区。然后,在 TDataSet.Resync 中,您会注意到
if rmCenter in Mode then
Count := (FBufferCount - 1) div 2 else
Count := FActiveRecord;
然后看看后面例程中Count是如何使用的;你的理论可能是正确的。也许在“if cmCenter in Mode”上放置一个断点,看看它是否从网格运行的地方被调用。
顺便说一句#2,即使这段代码没有帮助,这篇文章可能会有所帮助http://delphi.about.com/od/usedbvcl/l/aa011004a.htm