0

我有一个程序必须在用户转到数据集中的某个记录(TUniQuery)后设置一个更简单的屏幕。用户可以通过多种方式跳转:组合框、搜索框,最后是 DbNavigator。从第一个记录到最后一个记录需要很长时间。在执行程序之后,我发现按下 DBNavigator 上的最后一个按钮会导致数据集上的每条记录都被访问,因此,在每条记录上,屏幕都是无用的。从任何记录到第一个或最后一个记录都是一样的。我的理解是,这样的方法(FirstLast)会直接跳转。也许是 Unidac 组件上的一种特殊行为,但我找不到任何参考或属性来修改它。目前,我打算在BeforeScroll上设置一个标志事件,但由于AfterScroll也发生在每个事件之后,我无法知道数据集何时结束滚动。我也找不到任何关于 Delphi 文档的参考,这些参考只是说明
Call Last 使数据集中的最后一条记录处于活动状态

4

1 回答 1

0

按照源代码,我发现了这个:
简短的回答:是的,TDataset 尽可能直接跳转到第一条和最后一条记录,但是......

长答案:Delphi 具有广播的内部数据事件(DB单元,TDataEvent),因此数据感知控件可以正确更新。NextPrior和许多其他方法使用函数MoveBy 它生成一个Before/AfterScroll事件以及内部事件。如果需要, MoveBy可能会获取一些额外的行。假设您从 30 行数据集中提取了 20 行。你目前的记录是十五。你打电话给MoveBy(2)。不出所料,你的新纪录是十七岁。在这种情况下,会发生以下情况:

  1. 滚动前
  2. ActiveRecord 递增两次(在循环中),但未获取任何行
  3. 内部数据事件:deDataSetScroll
  4. 后滚动

请注意,滚动时不会发生任何程序事件。BeforeScroll出现在记录十五和AfterScroll在十七。

现在,同样的场景,但这次调用了MoveBy(10)。已经提取了五行(20 - 15),还需要检索另外五行。在这种情况下,顺序是:

  1. 滚动前
  2. ActiveRecord 递增 5(在循环中),但未获取任何行
  3. 在同一个循环中,活动记录增加 5。在每个循环中,获取一条记录(不发生程序事件)
  4. 内部数据事件:deDataSetChange
  5. 后滚动

每件事都运行良好。不使用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应该检查是否确实检索到了记录。

有人会说这很容易由我自己实现,并且确实是当您的代码滚动浏览数据集时。但在TDBNavigatorTJvDBSearchComboBox的情况下,我无法控制它们。其他人会说不要使用 JVCL,这里没有简单的答案。此外,仅供参考,一种方法表现出相同的行为。

于 2020-05-28T17:23:56.683 回答