Подтвердить что ты не робот

Как определить, когда пользователь завершил редактирование ячейки TStringGrid?

Я хочу вернуть содержимое ячейки в сетке строк, когда пользователь закончит ввод данных. Пользователь завершается нажатием клавиши ввода на клавиатуре или одним или двумя щелчками мыши на другой ячейке.

В Lazarus существует метод FinishedCellEditing, но не в Delphi. Как я могу обнаружить его в Delphi?

4b9b3361

Ответ 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

В принципе, есть много способов, которыми пользователь может закончить редактирование, и не все они всегда являются хорошей точкой перехвата:

  • он перемещает фокус на другую ячейку в сетке.
  • он перемещает фокус на другой элемент управления в форме
  • он перемещает фокус в другую форму.
  • он перемещает фокус в другое приложение.

Вам нужно спросить себя, при каких обстоятельствах вы хотите обновить контент.

Например: вы хотите обновить его, когда пользователь отменяет модальную форму или заканчивает приложение?

- Йерун