如何检测用户何时完成编辑TStringGrid单元格?

时间:2011-02-20 07:26:35

标签: delphi events tstringgrid

当用户完成输入数据时,我想返回字符串网格中单元格的内容。按下键盘上的回车键,或单击或双击另一个单元格时,用户即完成。

在Lazarus中有一种FinishedCellEditing的方法,但在Delphi中则没有。如何在Delphi中检测到它?

7 个答案:

答案 0 :(得分:5)

我有相同的问题,但更容易解决,因为我强迫用户按Enter键...

技巧:我在编辑其中时不让用户更改为另一个单元格,因此我强制用户必须按Intro / Enter结束编辑,然后我允许更改为其他单元格

不好的部分是OnKeyPress发生在OnSetEditText之前,所以我尝试使用OnKeyUp ......

我发现只是在编辑单元格时,按Enter / Intro后,OnKeyUp没有被触发......这是VCL上的一个BUG ...一个键已被释放而OnKeyUp没有被触发。< / p>

所以,我做了另一个技巧绕过那个...使用一个Timer来区别我会做的一点点,所以我让事件OnSetEditText的时间被触发。

让我解释一下我为成功做了些什么......

我已经通过在OnSelectCell上放置代码来锁定选择另一个单元格,与此类似:

CanSelect:=Not UserIsEditingOneCell;

在OnSetEditText上我输入如下代码:

UserIsEditingOneCell:=True;

所以现在,需要的是检测用户何时按下Enter / Intro ...并且我发现了一个可怕的事情,因为我说... OnKeyUp并没有被解雇这样的密钥...所以,我将模拟那个通过使用Timer并使用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

这有效,但我知道这很可怕,因为我需要使用一个计时器...但我不知道更好的方法...而且因为我不需要让用户去另一个单元格而edinting没有有效值......我可以使用它。

为什么在地狱里没有像OnEndingEditing这样的事件?

PD:我还注意到OnSetEditText对于每个被按下的键被多次触发,并且在Value参数上有不同的值...至少在OnGetEditMask事件上设置EditMask值'00:00:00'时。

答案 1 :(得分:4)

使用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 :(得分:2)

我通过响应发送到inplace编辑器的WM_KILLFOCUS消息来做到这一点。我必须继承inplace编辑器才能实现这一目标。

我从Raymond Chen的博客中了解到,如果您随后执行更改焦点的验证,则不适合。

答案 3 :(得分:2)

这是最终版本......哇,我改进了自己的代码(我之前提到的另一篇文章是我多年来使用的代码,直到今天......我看到了这篇文章,我把代码放到了我的代码中。 ..然后我试着修复自己的代码,我知道了,哇!我多年来一直在努力,现在我终于明白了。)

这是非常棘手的,我怎么能想象在编辑器处于活动状态时可以选择一个单元?

让我们看看如何做到这一点:

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

请对不起其他帖子...其他代码是我多年来使用的那个,因为我没有得到任何更好的解决方案......直到现在。

我希望这有助于其他人。

答案 4 :(得分:1)

最好只使用virtual string grid,因为Delphi中的字符串网格控件似乎并不能很好地支持这一点。

答案 5 :(得分:1)

<强> SOLUTION:

  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;

答案 6 :(得分:0)

基本上,用户可以通过多种方式结束编辑,而且并非所有这些方式都是一个很好的拦截点:

  1. 它将焦点移动到网格中的另一个单元格
  2. 它将焦点移动到表单上的另一个控件
  3. 它将焦点移到另一种形式
  4. 它将焦点移到另一个应用程序。
  5. 您需要问自己,您希望在哪种情况下更新内容。

    例如:当用户取消模态表单或结束应用程序时,是否要更新它?

    - 的Jeroen