Как я могу определить, когда пользователь завершил редактирование ячейки TStringGrid? - PullRequest
12 голосов
/ 20 февраля 2011

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

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

Ответы [ 7 ]

5 голосов
/ 23 октября 2012

У меня такая же проблема, но более простое решение, так как я заставляю пользователя нажимать клавишу Enter ...

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

Плохая часть в том, что OnKeyPress происходит до OnSetEditText, поэтому я попытался с OnKeyUp ...

И я обнаружил, что при редактировании ячейки, после нажатия Enter / Intro, OnKeyUp не запускается ... это ошибка на 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

На таком событии таймера:

UserIsEditingOneCell:=False;
// Do whatever needed just after the user has finished editing a cell

Это работает, но я знаю, что это ужасно, потому что мне нужно использовать таймер ... но я не знаю лучшего способа ... и так как мне нужно не позволить пользователю перейти в другую ячейку, пока та, которая редактирование не имеет допустимого значения ... я могу использовать это.

Почему, черт возьми, нет такого события, как OnEndingEditing?

PD: Я также заметил, что OnSetEditText запускается несколько раз для каждой нажатой клавиши и с различным значением в параметре Value ... по крайней мере при работе со значением EditMask '00: 00: 00 ', установленным для события OnGetEditMask.

4 голосов
/ 20 февраля 2011

Для 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;
2 голосов
/ 02 апреля 2013

Это окончательная версия ... Вау, я улучшил свой собственный код (другой пост, который я поместил ранее, был кодом, который я использовал в течение многих лет до сегодняшнего дня ... Я увидел этот пост и поставил свой код.... затем я попытался исправить свой собственный код, и я получил его, вау! Я пытался это годами, теперь я наконец получил его).

Это довольно сложно с тех пор, как, черт возьми, я могпредставьте себе, что ячейка может быть выбрана с активным редактором?

Давайте посмотрим, как это сделать:

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.

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

Надеюсь, это поможет другим.

2 голосов
/ 20 февраля 2011

Я делаю это, отвечая на сообщения WM_KILLFOCUS, отправленные редактору на месте.Я должен создать подкласс для редактора на месте, чтобы это произошло.

Я понимаю из блога Рэймонда Чена, что это не подходит, если вы затем выполняете проверку, которая меняет фокус.

1 голос
/ 07 октября 2015

РЕШЕНИЕ:

  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;
1 голос
/ 20 октября 2014

Вероятно, лучше всего использовать виртуальную строковую сетку , так как элемент управления сеточной строкой в ​​Delphi, похоже, не очень хорошо это поддерживает.

0 голосов
/ 21 февраля 2011

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

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

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

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

- Йерун

...