Многие разработчики, работающие с компонентом TStringGrid в Delphi и Lazarus, сталкиваются с проблемой программного перемещения курсора в нужную ячейку после завершения редактирования. Как показывает обсуждение на форуме, пользователь bowlofpetunias хотел переместить курсор в ячейку с неверными данными, чтобы пользователь мог их исправить, но столкнулся с трудностями.
Анализ исходного кода
В представленном коде используется метод GoToColRow, который пытается установить позицию курсора:
Procedure TForm1.GoToColRow(col, row : integer);
begin
stringgrid1.Col := col;
stringgrid1.Row := row;
col := stringgrid1.Col;
row := stringgrid1.Row;
end;
Однако этот код не работает как ожидается, потому что сразу после события OnEditingDone StringGrid автоматически перемещает курсор в зависимости от действий пользователя (например, при нажатии Enter или стрелок).
Решение с использованием таймера
Как правильно заметил Handoko, для корректной работы необходимо добавить небольшую задержку перед перемещением курсора. Это можно реализовать с помощью компонента TTimer:
procedure TForm1.StringGrid1EditingDone(Sender: TObject);
var
bng: real;
acol, arow, code: integer;
begin
StaticText1.Caption := '';
acol := stringgrid1.Col;
arow := stringgrid1.Row;
Val(stringgrid1.Cells[acol, arow], bng, code);
if bng > 360 then
begin
Windows.beep(500, 200);
StaticText1.Caption := '... illegal value';
// Устанавливаем таймер для отложенного перемещения
Timer1.Interval := 100; // небольшая задержка
Timer1.Tag := acol * 1000 + arow; // сохраняем координаты в Tag
Timer1.OnTimer := @TimerMoveToCell;
Timer1.Enabled := True;
end;
end;
procedure TForm1.TimerMoveToCell(Sender: TObject);
var
col, row: Integer;
begin
Timer1.Enabled := False;
// Извлекаем координаты из Tag
col := Timer1.Tag div 1000;
row := Timer1.Tag mod 1000;
// Перемещаем курсор
StringGrid1.Col := col;
StringGrid1.Row := row;
StringGrid1.SetFocus;
end;
Альтернативное решение без таймера
Если вы не хотите использовать таймер, можно попробовать перенаправить фокус через очередь сообщений:
procedure TForm1.StringGrid1EditingDone(Sender: TObject);
var
bng: real;
acol, arow, code: integer;
begin
StaticText1.Caption := '';
acol := stringgrid1.Col;
arow := stringgrid1.Row;
Val(stringgrid1.Cells[acol, arow], bng, code);
if bng > 360 then
begin
Windows.beep(500, 200);
StaticText1.Caption := '... illegal value';
// Отложенное перемещение через очередь сообщений
Application.QueueAsyncCall(@MoveToCellAsync, acol * 1000 + arow);
end;
end;
procedure TForm1.MoveToCellAsync(Data: PtrInt);
var
col, row: Integer;
begin
col := Data div 1000;
row := Data mod 1000;
StringGrid1.Col := col;
StringGrid1.Row := row;
StringGrid1.SetFocus;
end;
Дополнительные советы по работе с StringGrid
Проверка ввода данных:
procedure TForm1.StringGrid1ValidateEntry(sender: TObject; aCol, aRow: Integer;
const OldValue: string; var NewValue: String);
var
val: Double;
code: Integer;
begin
if TryStrToFloat(NewValue, val) then
begin
if (val > 360) then
begin
MessageDlg('Значение не может превышать 360', mtError, [mbOK], 0);
NewValue := OldValue;
end;
end
else
begin
MessageDlg('Введите числовое значение', mtError, [mbOK], 0);
NewValue := OldValue;
end;
end;
procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then // Enter
begin
if StringGrid1.Col < StringGrid1.ColCount - 1 then
StringGrid1.Col := StringGrid1.Col + 1
else if StringGrid1.Row < StringGrid1.RowCount - 1 then
begin
StringGrid1.Col := 1;
StringGrid1.Row := StringGrid1.Row + 1;
end;
Key := #0;
end;
end;
Заключение
Проблема перемещения курсора в StringGrid после редактирования решается добавлением небольшой задержки перед установкой позиции. Это можно реализовать как через TTimer, так и через очередь сообщений Application.QueueAsyncCall. Выбор метода зависит от ваших предпочтений и конкретных требований приложения.
Для более надежной валидации данных рекомендуется использовать событие OnValidateEntry, которое позволяет проверять ввод непосредственно во время редактирования ячейки.
Контекст описывает решение проблемы программного перемещения курсора в нужную ячейку компонента StringGrid в Delphi и Pascal с использованием таймера или очереди сообщений после завершения редактирования.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.