При разработке приложений для работы с базами данных на Delphi разработчики часто сталкиваются с ситуацией, когда события элементов управления срабатывают неожиданным образом. В данном случае пользователь столкнулся с проблемой, когда событие поиска (OnEditingDone компонента TEdit) вызывалось при нажатии кнопок "Next" и "Prev" (события OnClick компонентов TArrow).
procedure TForm1.edtSearchEditingDone(Sender: TObject);
begin
// Код поиска, который выполняется не только при завершении редактирования,
// но и при нажатии кнопок навигации
PerformSearch(edtSearch.Text);
end;
procedure TForm1.btnNextClick(Sender: TObject);
begin
// Переход к следующей записи
MoveToNextRecord;
end;
Почему это происходит?
Событие OnEditingDone срабатывает не только при явном завершении редактирования (например, нажатии Enter или потере фокуса), но и при программном изменении фокуса на другой элемент управления. Когда пользователь нажимает кнопку "Next" или "Prev", фокус сначала переходит с поля поиска на кнопку, что и вызывает событие OnEditingDone.
Решение с использованием QueueAsyncCall
Один из эффективных способов решения этой проблемы - использование механизма асинхронных вызовов через Application.QueueAsyncCall. Этот метод позволяет отложить выполнение поиска до завершения обработки текущих событий.
type
TForm1 = class(TForm)
// ... другие объявления
private
FSearchPending: Boolean;
FNavigationInProgress: Boolean;
procedure DoPerformSearch(Data: PtrInt);
end;
procedure TForm1.edtSearchEditingDone(Sender: TObject);
begin
if FNavigationInProgress then
Exit;
FSearchPending := True;
Application.QueueAsyncCall(@DoPerformSearch, 0);
end;
procedure TForm1.DoPerformSearch(Data: PtrInt);
begin
if not FSearchPending then Exit;
FSearchPending := False;
PerformSearch(edtSearch.Text);
end;
procedure TForm1.btnNextClick(Sender: TObject);
begin
// Отменяем ожидающий поиск
FSearchPending := False;
FNavigationInProgress := True;
try
Application.RemoveAsyncCalls(Self); // Удаляем все асинхронные вызовы для формы
MoveToNextRecord;
finally
FNavigationInProgress := False;
end;
end;
Альтернативные решения
1. Использование таймера
Можно использовать TTimer для задержки выполнения поиска:
procedure TForm1.edtSearchChange(Sender: TObject);
begin
// Сбрасываем таймер при каждом изменении текста
SearchTimer.Enabled := False;
SearchTimer.Interval := 500; // Задержка 500 мс
SearchTimer.Enabled := True;
end;
procedure TForm1.SearchTimerTimer(Sender: TObject);
begin
SearchTimer.Enabled := False;
PerformSearch(edtSearch.Text);
end;
procedure TForm1.btnNextClick(Sender: TObject);
begin
SearchTimer.Enabled := False; // Отменяем поиск при навигации
MoveToNextRecord;
end;
2. Явное подтверждение поиска
Можно требовать от пользователя явного подтверждения поиска (например, кнопкой или нажатием Enter):
procedure TForm1.edtSearchKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then // Enter
begin
Key := #0;
PerformSearch(edtSearch.Text);
end;
end;
procedure TForm1.btnSearchClick(Sender: TObject);
begin
PerformSearch(edtSearch.Text);
end;
3. Проверка изменения текста
Можно отслеживать, действительно ли изменился текст для поиска:
procedure TForm1.edtSearchEditingDone(Sender: TObject);
begin
if edtSearch.Text <> FLastSearchText then
begin
FLastSearchText := edtSearch.Text;
PerformSearch(FLastSearchText);
end;
end;
Оптимизация для медленных систем
Если ваше приложение будет работать на медленных компьютерах, стоит учитывать следующие рекомендации:
Минимизация запросов к БД: Кэшируйте результаты, используйте оптимальные SQL-запросы
Блокировка интерфейса: При выполнении длительных операций блокируйте элементы управления
Индикация прогресса: Показывайте пользователю, что операция выполняется
procedure TForm1.PerformSearch(const AText: string);
begin
Screen.Cursor := crHourGlass;
try
DisableControls;
try
// Выполнение поиска
qrySearch.Close;
qrySearch.SQL.Text := 'SELECT * FROM table WHERE field LIKE :text';
qrySearch.ParamByName('text').AsString := '%' + AText + '%';
qrySearch.Open;
finally
EnableControls;
end;
finally
Screen.Cursor := crDefault;
end;
end;
Заключение
Проблема неожиданного срабатывания событий - распространённая ситуация при разработке Delphi-приложений. Использование механизма QueueAsyncCall предоставляет элегантное решение, позволяющее управлять порядком выполнения операций. Альтернативные подходы, такие как таймеры или явное подтверждение действий, также могут быть эффективны в зависимости от конкретных требований приложения.
Главное - понимать последовательность срабатывания событий в Delphi и проектировать обработчики с учётом возможных побочных эффектов. Это особенно важно при работе с базами данных, где лишние запросы могут существенно снизить производительность системы.
Описание методов предотвращения непреднамеренного срабатывания событий в Delphi при взаимодействии с базой данных.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS