Delphi XE6 Grid Index Out of Range Error with TValueListEditor with docolumnTitles DisplayOption set to False and one row only
Asked Answered
O

0

7

Create a form and place a TValueListEditor. Set the doColumnTitles display option to False. Try and delete the first row. You will get a 'Grid Index out of Bounds' error. It is an ugly pig and so I am here to ask if using a try except block is the only way to deal with it?

Background:

Row is inherited from TCustomGrid via TCustomDrawGrid (Line 493 VCL.grids.pas) The property getter is private variable FCurrent.Y, the type of which is a value/field from a two value record. I cannot find where the FCurrent value is set by default (so it integer defaults to 0). It is not clear what values exist if the TValueListEditor has only one row and/or if there are no rows selected.

In any case, I get a Grid Index Out of Range Error - presumably raised from the TCustomGrid - but only if there is only one row and/or I attempt a delete on the first row in the TValueListEditor with doColumnTitles := false. Delete on other rows is fine. Regardless of whether this is a bug or not it seems somewhat stupid and inconsistent.

Is there a reason why the TValueList Editor constructor has inherited RowCount := 2;?

The error is thrown by TValueListEditor.DeleteRow according to:

  try
if (Strings.Count = 0) or
   (ARow < 1) or (ARow > RowCount - FixedRows) then
  {$IF DEFINED(CLR)}
  raise EInvalidGridOperation.CreateRes(SIndexOutOfRange);
  {$ELSE}
  raise EInvalidGridOperation.CreateRes(@SIndexOutOfRange);
  {$ENDIF}

Strings.Delete(ARow - FixedRows);
  finally
    FDeleting := False;
  end;

(See VCL.Valedit.pas at 804)

Is it a problem that there is an exception if (ARow < 1)?

Otherwise, perhaps it is that FixedRows is set in the constructor of the TCustomGrid:

constructor TCustomGrid.Create(AOwner: TComponent);

const GridStyle = [csCaptureMouse, csOpaque, csDoubleClicks,

                csNeedsBorderPaint, csPannable, csGestures];
begin
  inherited Create(AOwner);
  if NewStyleControls then
    ControlStyle := GridStyle
  else
    ControlStyle := GridStyle + [csFramed];
  FCanEditModify := True;
  FColCount := 5;
  FRowCount := 5;
  FFixedCols := 1;
  FFixedRows := 1;

...

and the comments in the header of VCL.grids.pas say "FixedRows The number of non-scrolling rows. This value must be at least one below RowCount." (Line 138).

Methinks that there ends up being a problem with these values when the titles row is removed from the TValueListEditor

Anyway. My question is just - Does this require the use of an exception handler - or is there a more elegant way of un-kludging it?

Here is some basic no seatbelts code:

unit vleDebug;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Buttons, Vcl.StdCtrls, Vcl.Grids, Vcl.ValEdit;

type
  TForm1 = class(TForm)
    vleWhoCaresNerd: TValueListEditor;
    Button1: TButton;
    BitBtn1: TBitBtn;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}

    procedure TForm1.BitBtn1Click(Sender: TObject);
    begin    
      vleWhoCaresNerd.DeleteRow(vleWhoCaresNerd.Row);
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var
     vGUID: TGUID;
    begin
      vleWhoCaresNerd.InsertRow('id', GUIDToString(vGUID), True);
    end;

    end.

Here's the .dfm:

 object Form1: TForm1
  Left = 0
  Top = 0
  Caption = ':P'
  ClientHeight = 568
  ClientWidth = 633
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 120
  TextHeight = 16
  object Label1: TLabel
    Left = 295
    Top = 162
    Width = 242
    Height = 16
    Caption = 'Select and delete the first row if you dare.'
  end
  object Label2: TLabel
    Left = 63
    Top = 190
    Width = 393
    Height = 16
    Caption = 
      'Delete other rows if you like. Make some with  the  Add GUID But' +
      'ton.'
  end
  object vleWhoCaresNerd: TValueListEditor
    Left = 8
    Top = 8
    Width = 617
    Height = 145
    DisplayOptions = [doAutoColResize, doKeyColFixed]
    Strings.Strings = (
      'jsonrpc=2.0')
    TabOrder = 0
    ColWidths = (
      150
      461)
  end
  object Button1: TButton
    Left = 8
    Top = 159
    Width = 75
    Height = 25
    Caption = 'Add GUID'
    TabOrder = 1
    OnClick = Button1Click
  end
  object BitBtn1: TBitBtn
    Left = 550
    Top = 159
    Width = 75
    Height = 25
    Caption = 'bbDelete'
    TabOrder = 2
    OnClick = BitBtn1Click
  end
end
Octahedron answered 29/7, 2014 at 6:0 Comment(4)
I have a feeling that grids don't like to be empty and I recall adding code to my grid to work around thatSleepy
Yes. I see that this grid doesn't like to be empty. I was just checking that such a kludge is necessary. Bummer David.Octahedron
I am also thinking that the kludge should be built into the TValueListEditor so we dumbass app developers don't have to deal with it.Octahedron
Yes, I can see in my own grid, code to work around this issue. I guess you'll need to do likewise.Sleepy

© 2022 - 2024 — McMap. All rights reserved.