Следуя исходному коду, я нашел это:
Короткий ответ : Да, TDataset переходит непосредственно к первой и последней записи, когда это возможно, но ...
Длинный ответ : Delphi имеет внутренние события данных ( DB unit, TDataEvent ), которые транслируются, поэтому элементы управления с учетом данных могут обновляться должным образом. Такие методы, как Next , Prior и многие другие, используют функцию MoveBy , которая генерирует события Before / AfterScroll , а также внутренние события. MoveBy может при необходимости получить дополнительные строки. Допустим, у вас есть двадцать строк, извлеченных из набора данных из тридцати строк. Ваш текущий рекорд - пятнадцать. Вы звоните MoveBy (2) . Как и ожидалось, ваш новый рекорд - семнадцать. В этом случае происходит следующее:
- BeforeScroll
- ActiveRecord увеличивается дважды (в al oop), но строки не выбираются
- Internal DataEvent: deDataSetScroll
- AfterScroll
Обратите внимание, что при прокрутке не происходит никаких программных событий. BeforeScroll встречается на пятнадцатой записи и AfterScroll на семнадцатой.
Теперь тот же сценарий, но на этот раз вызывается MoveBy (10) . Уже выбраны пять строк (20–15), и необходимо получить еще пять. В этом случае последовательность следующая:
- BeforeScroll
- ActiveRecord увеличивается на пять (в al oop), но строки не выбираются
- Активная запись увеличивается на пять, там же l oop. В каждом цикле выбирается запись (программных событий не происходит)
- внутреннее событие DataEvent: deDataSetChange
- AfterScroll
Все работает нормально . Проблема возникает с методом Last , который не использует MoveBy и предполагает, что записи были всегда извлечены:
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» l oop для обновления его содержимого и создания событий Before / AfterScroll .
Мне нравится Delphi, но это одна из вещей, которые мне не нравятся в наборах данных. Программа не может узнать, было ли инициировано определенное событие данных из-за действия пользователя или самой программы. В презентациях Delphi я предлагал способ перемещения по набору данных без запуска событий (как это делает MoveBy ), а затем такие компоненты, как TJvDBSearchComboBox , могли бы его использовать, или как минимум флаг или что-то подобное в наборе данных, который можно было бы включить перед началом посещения каждой записи. Кроме того, Last должен проверить, действительно ли были получены записи.
Некоторые скажут, что это легко реализовать самостоятельно, и это действительно так, когда ваш код прокручивает набор данных. Но в случае TDBNavigator и TJvDBSearchComboBox я не могу их контролировать. Кто-то другой скажет, что не используйте JVCL, здесь нет простого ответа. Кроме того, к вашему сведению, метод First демонстрирует такое же поведение.