TTMSFMXGrid: issue with RowSelectedCount when Enabled = False

Hello,

I am trying to create the ability to select a range of TTMSGMXGrid rows by a user double-clicking on the first and then the last cell of the range, and then use the selection later in the app. However, it appears that the RowSelectCount property does not get set correctly (equals 1 and only sees the last row of the range). Important: it works fine when I have the Enabled property of the grid under Options/Editing set to True, but does not work when it's set to False (I need it to be set to False because users shouldn't be able to edit the grid contents). I do have the SelectionMode feature set to smDisjunctRow. Also I am attaching the code I am using to programmatically select the range of rows (Unit1.pas, procedure TForm1.grid1CellDblClick of the attachment) - this is my simple test project I put together for research purposes. First the user selects a range by double-clicking on first and last row of the desired range (the values in clicked cells show up in the edits), and then clicks Button1 to show the string built from the values in selected cells.
If you need me to send you the whole project, please let me know which email address to use and I will send it.
Please let me know if this can be fixed and if there is a workaround I can use for now. Thanks.

Unit1.pas (2.2 KB)

The RowSelectionCount only returns values when disjunct selecting via the grid built-in selection system. If you are double-clicking and using your own implementation this setting is ignored. Depending on the way you are setting the selection. If you are using the SelectCell with shift parameter, then you can use TMSFNCGrid1.Selection property, that will return the range that has programmatically been selected by your code.

Thank you for the response. I use SelectRows() and pass it a range. It is interesting, however, that when I switch Enabled to True on the grid, the RowSelectionCount does get set properly, I am only having this problem when Enabled is False. Thoughts?

Can you post a sample / code snippet? We'll investigate what exactly is going on. You can attach a sample here.

Unit1.pas (2.2 KB)

Here you go. I did attach it to the original request but I can try again. In case the upload didn't work, here is the code below:

unit Unit1;

interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Edit,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.TMSBaseControl, FMX.TMSGridCell,
FMX.TMSGridOptions, FMX.TMSGridData, FMX.TMSCustomGrid, FMX.TMSGrid;

type
TForm1 = class(TForm)
grid1: TTMSFMXGrid;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
procedure FormCreate(Sender: TObject);
procedure grid1CellDblClick(Sender: TObject; ACol, ARow: Integer);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
holdRow: integer;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
var mystr : string;
i : integer;
row : integer;
begin
// Build a string of contents of the first column of selected range, show it
if (grid1.RowSelectionCount = 0) then
showmessage('Select rows')
else
begin
for i := 0 to grid1.RowSelectionCount - 1 do
begin
row := grid1.SelectedRow[i];
mystr := mystr + grid1.Cells[0,row] + '; ';
end;
showmessage(mystr);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i : integer;
begin
// Fill the grid
for i := 0 to 100 do
grid1.Cells[0,i] := i.ToString;
end;

procedure TForm1.grid1CellDblClick(Sender: TObject; ACol, ARow: Integer);
var row1 : integer;
row2 : integer;
begin
if (edit1.Text = '') or (edit2.Text <> '') then
begin
// Unselect any rows currently selected
row1 := grid1.SelectedRow[0];
row2 := grid1.SelectedRow[grid1.RowSelectionCount - 1];
grid1.UnSelectRows(row1,row2);
// place contents of first cell in selection into edit
edit1.Text := grid1.Cells[0,ARow];
edit2.Text := '';
// Save first row of selection
holdRow := ARow;
end
else if edit2.Text = '' then
begin
edit2.Text := grid1.Cells[0,ARow];
// Select the range of rows
if holdRow <= ARow then
grid1.SelectRows(holdRow,ARow)
else
grid1.SelectRows(ARow,holdRow);
end;

end;

end.

If you use SelectRows, it internally sets the Selection property. The Selection property can be used to retrieve the selected rows.

Thank you, I will try that and let you know how it goes.

Update: it did not work. Below is my code using Selection property. On the ButtonClick event, I assign grid1.Selection.StartRow to a variable sr, grid1.Selection.EndRow to er, then display them in a message, and they both end up with the same value (ex.: if I select rows 2 through 5, both start row and end row equal 5). What am I doing wrong?
Note: in this case, unlike with RowSelectionCount, this code still DOES NOT work even when I have Enabled set to True (this feature seems to not have any effect here, while in the initial example it does).

unit Unit1;

interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Edit,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.TMSBaseControl, FMX.TMSGridCell,
FMX.TMSGridOptions, FMX.TMSGridData, FMX.TMSCustomGrid, FMX.TMSGrid;

type
TForm1 = class(TForm)
grid1: TTMSFMXGrid;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
procedure FormCreate(Sender: TObject);
procedure grid1CellDblClick(Sender: TObject; ACol, ARow: Integer);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
holdRow: integer;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
var mystr : string;
i : integer;
row : integer;
sr : integer;
er : integer;
rowcnt : integer;
begin
// Build a string of contents of the first column of selected range, show it
sr := grid1.Selection.StartRow;
er := grid1.Selection.EndRow;
showmessage(sr.ToString + ':' + er.ToString);
rowcnt := er - sr;
if (rowcnt = 0) then
showmessage('Select rows')
else
begin
for i := 0 to rowcnt - 1 do
begin
row := grid1.SelectedRow[i];
mystr := mystr + grid1.Cells[0,row] + '; ';
end;
showmessage(mystr);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i : integer;
begin
// Fill the grid
for i := 0 to 100 do
grid1.Cells[0,i] := i.ToString;
end;

procedure TForm1.grid1CellDblClick(Sender: TObject; ACol, ARow: Integer);
var row1 : integer;
row2 : integer;
begin
if (edit1.Text = '') or (edit2.Text <> '') then
begin
// Unselect any rows currently selected
row1 := grid1.SelectedRow[0];
row2 := grid1.SelectedRow[grid1.RowSelectionCount - 1];
grid1.UnSelectRows(row1,row2);
// place contents of first cell in selection into edit
edit1.Text := grid1.Cells[0,ARow];
edit2.Text := '';
// Save first row of selection
holdRow := ARow;
end
else if edit2.Text = '' then
begin
edit2.Text := grid1.Cells[0,ARow];
// Select the range of rows
if holdRow <= ARow then
grid1.SelectRows(holdRow,ARow)
else
grid1.SelectRows(ARow,holdRow);
end;

end;

end.

One more thing that may be helpful in your research. In my ButtonClick procedure (below), I replaced grid1.Selection.StartRow with grid1.SelectedRow[0], and looks like the functionality of StartRow[0] depends on whether or not Enabled property of grid1 is set to True. When it's set to True, SelectedRow[0] returns the correct result. When it's set to False, it does not return the correct result and returns the last row of the selection instead. Below is the code of ButtonClick. The code of the double-click procedure did not change. Any thoughts?

procedure TForm1.Button1Click(Sender: TObject);
var mystr : string;
i : integer;
row : integer;
sr : integer;
er : integer;
rowcnt : integer;
begin
// Build a string of contents of the first column of selected range, show it
sr := grid1.SelectedRow[0]; //grid1.Selection.StartRow;
er := grid1.Selection.EndRow;
showmessage(sr.ToString + ':' + er.ToString);
rowcnt := er - sr;
if (rowcnt = 0) then
showmessage('Select rows')
else
begin
for i := 0 to rowcnt - 1 do
begin
row := grid1.SelectedRow[i];
mystr := mystr + grid1.Cells[0,row] + '; ';
end;
showmessage(mystr);
end;
end;

Good morning, any update on this?

Hi,

We are currently investiging this.

The real issue is that you are trying to override the default behaviour of selection, so to completely handle this in the OnCellDblClick you'll need to switch to the Selection property, and you'll also have to turn off the internal selection. Below is a sample that demonstrates this.

unit Unit5;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, FMX.TMSUtils,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Edit,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.TMSBaseControl, FMX.TMSGridCell,
  FMX.TMSGridOptions, FMX.TMSGridData, FMX.TMSCustomGrid, FMX.TMSGrid;

type
  TForm5 = class(TForm)
    grid1: TTMSFMXGrid;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure grid1CellDblClick(Sender: TObject; ACol, ARow: Integer);
    procedure Button1Click(Sender: TObject);
    procedure grid1GetCellLayout(Sender: TObject; ACol, ARow: Integer;
      ALayout: TTMSFMXGridCellLayout; ACellState: TCellState);
    procedure grid1CellClick(Sender: TObject; ACol, ARow: Integer);
  private
    { Private declarations }
    FSelectionStarted, FDblClicked: Boolean;
  public
    { Public declarations }
  end;

var
  Form5: TForm5;
  holdRow: Integer;

implementation

{$R *.fmx}

procedure TForm5.Button1Click(Sender: TObject);
var
  mystr: string;
  i: Integer;
  row: Integer;
begin
  // Build a string of contents of the first column of selected range, show it
  for i := grid1.Selection.StartRow to grid1.Selection.EndRow do
  begin
    mystr := mystr + grid1.Cells[0, I] + '; ';
  end;

  showmessage(mystr);
end;

procedure TForm5.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  // Fill the grid
  for i := 0 to 100 do
    grid1.Cells[0, i] := i.ToString;
  grid1.SelectionMode := smNone;
  grid1.Options.Editing.Enabled := False;
  FSelectionStarted := False;
end;

procedure TForm5.grid1CellClick(Sender: TObject; ACol, ARow: Integer);
begin
  if not FSelectionStarted and not FDblClicked then
    grid1.Selection := CellRange(ACol, ARow, ACol, ARow)
  else
  begin
    FDblClicked := False;
    FSelectionStarted := False;
  end;
end;

procedure TForm5.grid1CellDblClick(Sender: TObject; ACol, ARow: Integer);
var
  row1: Integer;
  row2: Integer;
begin
  FDblClicked := True;
  if (Edit1.Text = '') or (Edit2.Text <> '') then
  begin
    Edit1.Text := grid1.Cells[0, ARow];
    Edit2.Text := '';
    holdRow := ARow;

    grid1.Selection := CellRange(ACol, ARow, ACol, Arow);
    FSelectionStarted := True;
  end
  else if Edit2.Text = '' then
  begin
    Edit2.Text := grid1.Cells[0, ARow];

    if holdRow <= ARow then
      grid1.Selection := CellRange(ACol, holdRow, ACol, ARow)
    else
      grid1.Selection := CellRange(ACol, ARow, ACol, holdRow);
  end;

end;

procedure TForm5.grid1GetCellLayout(Sender: TObject; ACol, ARow: Integer;
  ALayout: TTMSFMXGridCellLayout; ACellState: TCellState);
begin
  if (ARow >= grid1.Selection.StartRow) and not grid1.IsFixed(ACol, ARow) and (ARow <= grid1.Selection.EndRow) then
    ALayout.Assign(grid1.GetDefaultSelectedLayout.Layout);
end;

end.

OK, thank you for looking into this!