我有一个程序必须在用户转到数据集中的某个记录(TUniQuery)后设置一个更简单的屏幕。用户可以通过多种方式跳转:组合框、搜索框,最后是 DbNavigator。从第一个记录到最后一个记录需要很长时间。在执行程序之后,我发现按下 DBNavigator 上的最后一个按钮会导致数据集上的每条记录都被访问,因此,在每条记录上,屏幕都是无用的。从任何记录到第一个或最后一个记录都是一样的。我的理解是,这样的方法(First和Last)会直接跳转。也许是 Unidac 组件上的一种特殊行为,但我找不到任何参考或属性来修改它。目前,我打算在BeforeScroll上设置一个标志事件,但由于AfterScroll也发生在每个事件之后,我无法知道数据集何时结束滚动。我也找不到任何关于 Delphi 文档的参考,这些参考只是说明
Call Last 使数据集中的最后一条记录处于活动状态
1 回答
按照源代码,我发现了这个:
简短的回答:是的,TDataset 尽可能直接跳转到第一条和最后一条记录,但是......
长答案:Delphi 具有广播的内部数据事件(DB单元,TDataEvent),因此数据感知控件可以正确更新。Next,Prior和许多其他方法使用函数MoveBy ,它生成一个Before/AfterScroll事件以及内部事件。如果需要, MoveBy可能会获取一些额外的行。假设您从 30 行数据集中提取了 20 行。你目前的记录是十五。你打电话给MoveBy(2)。不出所料,你的新纪录是十七岁。在这种情况下,会发生以下情况:
- 滚动前
- ActiveRecord 递增两次(在循环中),但未获取任何行
- 内部数据事件:deDataSetScroll
- 后滚动
请注意,滚动时不会发生任何程序事件。BeforeScroll出现在记录十五和AfterScroll在十七。
现在,同样的场景,但这次调用了MoveBy(10)。已经提取了五行(20 - 15),还需要检索另外五行。在这种情况下,顺序是:
- 滚动前
- ActiveRecord 递增 5(在循环中),但未获取任何行
- 在同一个循环中,活动记录增加 5。在每个循环中,获取一条记录(不发生程序事件)
- 内部数据事件:deDataSetChange
- 后滚动
每件事都运行良好。不使用MoveBy并假定始终获取 记录的Last方法会出现问题:
procedure TDataSet.Last;
begin
CheckBiDirectional;
CheckBrowseMode;
DoBeforeScroll;
ClearBuffers;
try
InternalLast;
GetPriorRecord;
GetPriorRecords;
finally
FEOF := True;
DataEvent(deDataSetChange, 0); // Problem! Causes TJvDBSearchComboBox to retrieve all records (again)
DoAfterScroll;
end;
end;
如您所见,即使所有记录都已获取, Last方法也始终广播deDataSetChange事件。在我的四个记录的特殊情况下,用户按下TDBNavigator的最后一个按钮,该按钮又调用Last方法。广播deDataSetChange事件,然后TJvDBSearchComboBox(来自 JVCL 库)通过使用“while not eof...next”循环访问每一行来更新其内容并生成Before/AfterScroll事件来对此事件做出反应。
我喜欢 Delphi,但它是我不喜欢数据集的原因之一。程序无法了解是否由于用户操作或程序本身触发了某些数据事件。我在 Delphi 演示文稿中提出了一种在不触发事件的情况下遍历数据集的方法(如MoveBy所做的那样),然后像TJvDBSearchComboBox这样的组件可以使用它,或者至少可以打开数据集中的标志等在开始访问每条记录之前。此外,Last应该检查是否确实检索到了记录。
有人会说这很容易由我自己实现,并且确实是当您的代码滚动浏览数据集时。但在TDBNavigator和TJvDBSearchComboBox的情况下,我无法控制它们。其他人会说不要使用 JVCL,这里没有简单的答案。此外,仅供参考,第一种方法表现出相同的行为。