Как определить, когда пользователь завершил редактирование ячейки TStringGrid?
Я хочу вернуть содержимое ячейки в сетке строк, когда пользователь закончит ввод данных. Пользователь завершается нажатием клавиши ввода на клавиатуре или одним или двумя щелчками мыши на другой ячейке.
В Lazarus существует метод FinishedCellEditing, но не в Delphi. Как я могу обнаружить его в Delphi?
Ответы
Ответ 1
У меня есть та же проблема, но проще, потому что я заставляю пользователя нажимать клавишу Enter...
Трюк. Я не позволяю пользователю менять другую ячейку во время редактирования, поэтому я принуждаю пользователя к нажатию кнопки Intro/Enter для завершения редактирования, затем я могу перейти на другую ячейку.
Плохая часть заключается в том, что OnKeyPress происходит до OnSetEditText, поэтому я попытался с OnKeyUp...
И я обнаружил, что только при редактировании ячейки после нажатия Enter/Intro OnKeyUp не запускается... это BUG на VCL... ключ освобождается, а OnKeyUp не запускается.
Итак, я делаю еще один трюк, чтобы обойти это... использовать таймер для того, чтобы отличать то, что я сделал бы немного, поэтому я позволяю раньше времени запускать событие OnSetEditText.
Позвольте мне объяснить, что я сделал для успеха...
Я заблокировал выбор другой ячейки, поставив код OnSelectCell, очень похожий на это:
CanSelect:=Not UserIsEditingOneCell;
И на OnSetEditText я поставлю код следующим образом:
UserIsEditingOneCell:=True;
Итак, теперь нужно обнаружить, когда пользователь нажимает Enter/Intro... и я нашел ужасную вещь, как я сказал... OnKeyUp не уволен за такой ключ... поэтому я смогу имитировать это используя таймер и используя OnKeyPress, потому что OnKeyPress уволен, но OnKeyUp нет, для клавиши Enter...
Итак, на OnKeyPress я добавляю что-то вроде:
TheTimerThatIndicatesUserHasPressEnter.Interval:=1; // As soon as posible
TheTimerThatIndicatesUserHasPressEnter.Enabled:=True; // But after event OnSetEditText is fired, so not jsut now, let some time pass
An на таком событии таймера:
UserIsEditingOneCell:=False;
// Do whatever needed just after the user has finished editing a cell
Это работает, но я знаю, что это ужасно, потому что мне нужно использовать таймер... но я не знаю лучшего способа... и так как мне не нужно пропускать пользователя в другую ячейку, а тот, который edinting не имеет допустимого значения... я могу использовать это.
Почему, черт возьми, нет события, такого как OnEndingEditing?
PD: Я также заметил, что OnSetEditText запускается несколько раз для каждой нажатой клавиши и с другим значением параметра Value... по крайней мере, при работе с значением EditMask '00: 00: 00 ', установленным в событии OnGetEditMask.
Ответ 2
С VCL TStringGrid вам нужно событие OnSetEditText. Обратите внимание, что он срабатывает каждый раз, когда пользователь меняет что-то в любой ячейке. Итак, если вы только хотите что-то сделать после завершения редактирования пользователем, вам нужно будет просмотреть значения строк и столбцов параметров события. И, конечно же, вам нужно позаботиться о ситуации, когда пользователь закончит редактирование ячейки и не отредактирует другую ячейку, например, щелкнув за пределами TStringGrid. Что-то вроде:
TForm1 = class(TForm)
...
private
FEditingCol, FEditingRow: Longint;
...
end;
procedure Form1.DoYourAfterEditingStuff(ACol, ARow: Longint);
begin
...
end;
procedure Form1.StringGrid1OnEnter(...)
begin
EditingCol := -1;
EditingRow := -1;
end;
procedure Form1.StringGrid1OnSetEditText(Sender: TObject; ACol, ARow: Longint; const Value: string)
begin
if (ACol <> EditingCol) and (ARow <> EditingRow) then
begin
DoYourAfterEditingStuff(EditingCol, EditingRow);
EditingCol := ACol;
EditingRow := ARow;
end;
end;
procedure Form1.StringGrid1OnExit(...)
begin
if (EditingCol <> -1) and (EditingRow <> -1) then
begin
DoYourAfterEditingStuff(EditingCol, EditingRow);
// Not really necessary because of the OnEnter handler, but keeps the code
// nicely symmetric with the OnSetEditText handler (so you can easily
// refactor it out if the desire strikes you)
EditingCol := -1;
EditingRow := -1;
end;
end;
Ответ 3
Я делаю это, отвечая на сообщения WM_KILLFOCUS, отправленные редактору inplace. Мне нужно подклассифицировать редактор inplace, чтобы это произошло.
Я понимаю из блога Raymond Chen, что это не подходит, если вы затем выполните проверку, которая изменит фокус.
Ответ 4
Это окончательная версия... Вау, я улучшил свой собственный код (другой пост, который я поставил раньше, был кодом, который я использовал в течение многих лет до сегодняшнего дня... Я видел этот пост, и я поставил код, который у меня был... тогда я попытался исправить свой собственный код, и я получил его, ничего себе!, Я пытался это годами, теперь, наконец, получил его).
Это довольно сложно, так как, черт возьми, я мог представить, что ячейку можно было бы выбрать с активным редактором?
Посмотрите, как это сделать:
var
MyStringGrig_LastEdited_ACol, MyStringGrig_LastEdited_ARow: Integer;
//To remember the last cell edited
procedure TmyForm.MyStringGrigSelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
begin
//When selecting a cell
if MyStringGrig.EditorMode then begin //It was a cell being edited
MyStringGrig.EditorMode:= False; //Deactivate the editor
//Do an extra check if the LastEdited_ACol and LastEdited_ARow are not -1 already.
//This is to be able to use also the arrow-keys up and down in the Grid.
if (MyStringGrig_LastEdited_ACol <> -1) and (MyStringGrig_LastEdited_ARow <> -1) then
MyStringGrigSetEditText(Sender, MyStringGrig_LastEdited_ACol, MyStringGrig_LastEdited_ARow,
MyStringGrig.Cells[MyStringGrig_LastEdited_ACol, MyStringGrig_LastEdited_ARow]);
//Just make the call
end;
//Do whatever else wanted
end;
procedure TmyForm.MyStringGrigSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
begin
//Fired on every change
if Not MyStringGrig.EditorMode //goEditing must be 'True' in Options
then begin //Only after user ends editing the cell
MyStringGrig_LastEdited_ACol:= -1; //Indicate no cell is edited
MyStringGrig_LastEdited_ARow:= -1; //Indicate no cell is edited
//Do whatever wanted after user has finish editing a cell
end else begin //The cell is being editted
MyStringGrig_LastEdited_ACol:= ACol; //Remember column of cell being edited
MyStringGrig_LastEdited_ARow:= ARow; //Remember row of cell being edited
end;
end;
Это работает для меня как шарм.
Обратите внимание, что для сохранения последних отредактированных координат ячейки требуется две переменные.
Помните, что goEditing
должен быть True
в Options
.
Извините за другой пост... что другой код был тем, который я использовал в течение многих лет, так как я не получил лучшего решения... до сих пор.
Я надеюсь, что это поможет другим.
Ответ 5
Скорее всего, лучше всего использовать виртуальную сетку строк, поскольку управление сеткой строк в Delphi действительно не очень хорошо поддерживает это.
Ответ 6
РЕШЕНИЕ:
TMyGrid= class(TStringGrid)
private
EditorPrevState: Boolean; //init this to false!
EditorPrevRow : LongInt;
EditorPrevCol : LongInt;
procedure WndProc(VAR Message: TMessage); override;
procedure EndEdit (ACol, ARow: Longint); // the user closed the editor
etc
end;
constructor TMyGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
EditorPrevRow := Row;
EditorPrevCol := Col;
EditorPrevState:= false;
end;
procedure TMyGrid.WndProc(var Message: TMessage);
begin
inherited;
if EditorPrevState then { The editor was open }
begin
if NOT EditorMode then { And not is closed }
begin
EditorPrevState:= EditorMode;
EndEdit(EditorPrevCol, EditorPrevRow); <------ editor is closed. process the text here
end;
EditorPrevRow := Row;
EditorPrevCol := Col;
End;
EditorPrevState := EditorMode;
end;
procedure TMyGrid.EndEdit(aCol, aRow: Integer); { AlwaysShowEditror must be true in Options }
begin
Cells[ACol, ARow]:= StringReplace(Cells[ACol, ARow], CRLF, ' ', [rfReplaceAll]); { Replace ENTERs with space - This Grid cannot draw a text on multiple rows so enter character will he rendered as 2 squares. }
if Assigned(FEndEdit)
then FEndEdit(Self, EditorPrevCol, EditorPrevRow); // optional
end;
Ответ 7
В принципе, есть много способов, которыми пользователь может закончить редактирование, и не все они всегда являются хорошей точкой перехвата:
- он перемещает фокус на другую ячейку в сетке.
- он перемещает фокус на другой элемент управления в форме
- он перемещает фокус в другую форму.
- он перемещает фокус в другое приложение.
Вам нужно спросить себя, при каких обстоятельствах вы хотите обновить контент.
Например: вы хотите обновить его, когда пользователь отменяет модальную форму или заканчивает приложение?
- Йерун