Recent

Author Topic: [SOLVED] How to stay in the invalid cell in Event TStringGrid_OnEditingDone?  (Read 1953 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 849
My program has a StringGrid where 2 columns are editable. To validate the user input, I use Event StringGrid1EditingDone. When I detect a bad input, I show a Message and want the Cursor to stay in the invalid cell. How can this be done? Assigning StringGrid1.Col and StringGrid1.Row a value, does not work. The cursor moves always to the new cell, whereto the user moved.

I use Lazarus 2.0.10 with FPC 3.2.0. Thanks in advance.
« Last Edit: December 06, 2023, 04:14:41 pm by Hartmut »

jamie

  • Hero Member
  • *****
  • Posts: 6735
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #1 on: December 06, 2023, 02:00:49 am »
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Grids,LclIntf,Lmessages;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     StringGrid1: TStringGrid;
  16.     procedure StringGrid1EditingDone(Sender: TObject);
  17.   private
  18.  
  19.   public
  20.      Procedure ResetGridCell(Var Msg:TLmessage); Message LM_USER+1;
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. { TForm1 }
  31. PRocedure TForm1.ResetGridCell(Var Msg:TLmessage);
  32. Begin
  33.   If TObject(Msg.lParam) is TStringGrid Then With TStringGrid(Msg.Lparam) do
  34.    Begin
  35.      COL := Lo(DWord(Msg.wParam));
  36.      Row := Hi(Dword(Msg.wparam));
  37.    end;
  38. end;
  39.  
  40. procedure TForm1.StringGrid1EditingDone(Sender: TObject);
  41. begin
  42.   If Sender is TStringGrid then with TStringGrid(Sender) do
  43.   Begin
  44.     If Modified then {assumed an error for now as a test}
  45.      PostMessage(Self.handle, LM_USER+1, MakeLong(COL,ROW),PtrUint(Sender));
  46.     Modified := false;
  47.   end;
  48. end;
  49.  
  50. end.
  51.  

Time to learn some real code  ;D
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 6735
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #2 on: December 06, 2023, 02:21:15 am »
In case you want to enhance your editing mode.

Code: Pascal  [Select][+][-]
  1. PRocedure TForm1.ResetGridCell(Var Msg:TLmessage);
  2. Begin
  3.   If TObject(Msg.lParam) is TStringGrid Then With TStringGrid(Msg.Lparam) do
  4.    Begin
  5.      COL := Lo(DWord(Msg.wParam));
  6.      Row := Hi(Dword(Msg.wparam));
  7.      if Editor is TStringCellEditor Then
  8.       Begin
  9.        EditorMode := true;
  10.        TStringCellEditor(Editor).SelLength:=0;
  11.       end;
  12.    end;
  13. end;                                    
The only true wisdom is knowing you know nothing

egsuh

  • Hero Member
  • *****
  • Posts: 1493
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #3 on: December 06, 2023, 06:25:52 am »

https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tcustomgrid.setcolrow.html

There is an explanation of SetColRow method, but this is not compiled. TStringGrid is a descendant of TCustomGrid, but this method is "protected". Is there anyway to call this function, e.g. using class helper, etc.?

egsuh

  • Hero Member
  • *****
  • Posts: 1493
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #4 on: December 06, 2023, 06:48:06 am »
I think there should be someway to cancel or abort following operations at the position of comment, because moving to another cell (when an arrow is clicked) is performed after EditingDone event is handled. 
abort does not work.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1EditingDone(Sender: TObject);
  2. var
  3.    r, c: integer;
  4. begin
  5.    r := StringGrid1.Row;
  6.    c := StringGrid1.Col;
  7.  
  8.    if StrtoIntDef(StringGrid1.Cells[c, r], -1) = -1 then begin
  9.       ShowMessage('Only number please');
  10.       StringGrid1.Cells[c, r] := '';
  11.  
  12.       StringGrid1.Row := r;
  13.       StringGrid1.Col := c;
  14.  
  15.       // Someway to cancel following actions
  16.    end;
  17. end;

dsiders

  • Hero Member
  • *****
  • Posts: 1282
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #5 on: December 06, 2023, 07:19:39 am »
I think there should be someway to cancel or abort following operations at the position of comment, because moving to another cell (when an arrow is clicked) is performed after EditingDone event is handled. 
abort does not work.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1EditingDone(Sender: TObject);
  2. var
  3.    r, c: integer;
  4. begin
  5.    r := StringGrid1.Row;
  6.    c := StringGrid1.Col;
  7.  
  8.    if StrtoIntDef(StringGrid1.Cells[c, r], -1) = -1 then begin
  9.       ShowMessage('Only number please');
  10.       StringGrid1.Cells[c, r] := '';
  11.  
  12.       StringGrid1.Row := r;
  13.       StringGrid1.Col := c;
  14.  
  15.       // Someway to cancel following actions
  16.    end;
  17. end;

Have you noticed or tried OnValidateEntry?

Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1ValidateEntry(Sender: TObject; aCol, aRow: Integer;
  2.   const OldValue: string; var NewValue: String);
  3. var
  4.   IntVal: Integer;
  5. begin
  6.   if (ACol = 1) and (not TryStrToInt(NewValue, IntVal)) then
  7.   begin
  8.     NewValue := OldValue;
  9.     raise Exception.Create('Only numeric values in column 1');
  10.   end;
  11. end;

[ edit ]Sorry, I replied to the wrong person.[/ edit ]
« Last Edit: December 06, 2023, 07:23:42 am by dsiders »
Preview the next Lazarus documentation release at: https://dsiders.gitlab.io/lazdocsnext

wp

  • Hero Member
  • *****
  • Posts: 12469
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #6 on: December 06, 2023, 11:50:39 am »
dsiders is right. Let me explain: OnValidateEntry is fired from the boolean function TCustomGrid.ValidateEntry:
Code: Pascal  [Select][+][-]
  1. function TCustomGrid.ValidateEntry(const ACol, ARow: Integer;
  2.   const OldValue:string; var NewValue:string): boolean;
  3. begin
  4.   result := true;
  5.   if assigned(OnValidateEntry) then begin
  6.     try
  7.       OnValidateEntry(Self, ACol, ARow, OldValue, NewValue);
  8.     except
  9.       on E:Exception do begin
  10.         result := false;
  11.         if FGridState=gsSelecting then
  12.           FGridState := gsNormal;
  13.         Application.HandleException(E);
  14.       end;
  15.     end;
  16.   end;
  17. end;
As can be seen the return value of this function (which decides on further processing) becomes false only when the OnValidateEntry raises an exception. Therefore, you must do this in on OnValidateEntry handler if you consider the current input to be incorrect.

See also: https://wiki.freepascal.org/Grids_Reference_Page#Validating_Entered_Values


Hartmut

  • Hero Member
  • *****
  • Posts: 849
Re: How to stay in the invalid cell in Event TStringGrid_OnEditingDone?
« Reply #7 on: December 06, 2023, 04:14:16 pm »
Thank you very much to all for your posts and suggestions. I studied them all and learned from you.

I choosed jamie's solution because this was the 1st one which worked perfectly in my program.

Special thanks to wp for your explanations about Event OnValidateEntry and how it is embedded in TCustomGrid.ValidateEntry and the documentation link.

In the future I will try Event OnValidateEntry which I did not know before.

This is a great Forum!

egsuh

  • Hero Member
  • *****
  • Posts: 1493
Quote
Have you noticed or tried OnValidateEntry?

I didn't know this. Thanks a lot (to wp as well).

jamie

  • Hero Member
  • *****
  • Posts: 6735
Apparently, if you want to use the OValidate event, all one needs to do to reject the cell's contents and stay in place is simply all the "Abort" while in that event.
So if you don't like the new string, you can just "Abort" and it will just stay there in the cell.
The only true wisdom is knowing you know nothing

egsuh

  • Hero Member
  • *****
  • Posts: 1493

Quote
Apparently, if you want to use the OValidate event, all one needs to do to reject the cell's contents and stay in place is simply all the "Abort" while in that event.

Yes, this seems the neatest way. If exception is raised, there is an error message popup anyway.
Following seems the final. Thank you for your advice.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1ValidateEntry(Sender: TObject; aCol, aRow: Integer;
  2.   const OldValue: string; var NewValue: String);
  3. begin
  4.    if (NewValue <> '') and (StrtoIntDef(NewValue, -1) = -1) then begin
  5.       ShowMessage('Only numeric values');
  6.       NewValue := OldValue;
  7.       Abort;
  8.       // raise Exception.Create('Only numeric values in column 1');
  9.    end;
  10. end;
  11.  

 

TinyPortal © 2005-2018