Jump to content
Damon

Tstringgrid grid index out of bounds

Recommended Posts

Making me crazy.  I build grid at runtime based on a db record with col and row counts.  I display and populate strings with data for cells from detail table data.

When the grid is shown (pagecontrol tab is active) i want to programatically select the row that matches some data (id for a record) i know what col and row this is but when i try to set col and row it says out of bounds which is a lie.  I thought it maybe because of fixed row and col, but if i set col-1 and row-1 the result is the same.  😞  What am I missing?

This is to build the grid:

procedure Tmain.buildrackClick(Sender: TObject);
var
  i, k, c, r, j, box, rb, cb : Integer;
begin
  //showmessage('draw grid');
  stringgrid1.ColCount := ddata.rackclientdataset.fieldbyname('c').asinteger+1;
  stringgrid1.RowCount := ddata.rackclientdataset.fieldbyname('r').asinteger+1;
  setgridrow := 0;
  setgridcol := 0;
  j := 0;
  box := 0;
      K := 0;
      with StringGrid1 do
       for J:= 0 to RowCount - 1 do
         for I := 0 to ColCount - 1 do
          begin
            K := K + 1;
            if (i = 0) or (j = 0) then
              begin
                rb := 0;
              end else
              begin
                box := box + 1;
                ddata.tubclientdataset.locate('Rackid;Tubid',vararrayof([listbox1.items[listbox1.ItemIndex],box]),[]);
                if ddata.tubclientdataset.fieldbyname('id').asstring = '' then Cells[I,J] := 'Bin '+IntToStr(box) else
                  begin
                  Cells[I,J] := 'Bin '+IntToStr(box)+' '+ddata.tubclientdataset.fieldbyname('id').asstring;             
                  if (dbedit17.text = listbox1.items[listbox1.ItemIndex]) and (dbedit18.text = inttostr(box)) then
                      begin
                        setgridrow :=J;
                        setgridcol:=I;
                        //showmessage('cell is row '+inttostr(j)+' col '+inttostr(i));
                      end;
                  end;
              end;
          end;
    with StringGrid1 do
    for r := 1 to RowCount - 1 do
        begin
          Cells[0,r] := 'Row '+inttostr(r);
        end;
    with StringGrid1 do
    for c := 1 to ColCount - 1 do
        begin
          Cells[c,0] := 'Column '+inttostr(c);
        end;
   stringgrid1.defaultcolwidth := trunc((stringgrid1.Width-56)/stringgrid1.colcount);
   stringgrid1.defaultrowheight := trunc((stringgrid1.height-45)/stringgrid1.rowcount);
   stringgrid1.ColWidths[0] := 56;
end;

Then this is to select the cell and it will not work!  Fails when trying to set col value.  Can see valid grid cell on screen at time.  Doesn't make sense.  Vars have correct data.

if (setgridcol <> 0) and (setgridrow <> 0) then
        begin
          //stringgrid1.refresh;
          showmessage(inttostr(setgridcol)+' is col '+inttostr(setgridrow)+' is row - try to set cell selected.');
          //stringgrid1.SetFocus;
          showmessage('grid focused');
          stringgrid1.col := setgridcol;
          showmessage('col set');
          stringgrid1.row := setgridrow;
          showmessage('row set');
        end;

 

Share this post


Link to post
2 hours ago, Damon said:

I thought it maybe because of fixed row and col, but if i set col-1 and row-1 the result is the same.  😞 

Try something more like this:

procedure Tmain.buildrackClick(Sender: TObject);
var
  c, r, box : Integer;
  RackId : string;
begin
  //ShowMessage('draw grid');
  setgridrow := -1;
  setgridcol := -1;
  box := 0;
  RackId := ListBox1.Items[ListBox1.ItemIndex];
  with StringGrid1 do
  begin
    ColCount := 1{FixedCols} + ddata.RackClientDataSet.FieldByName('c').AsInteger;
    RowCount := 1{FixedRows} + ddata.RackClientDataSet.FieldByName('r').AsInteger;
    for c := 1{FixedCols} to ColCount - 1 do
    begin
      Cells[c, 0{FixedRows-1}] := 'Column ' + IntToStr(c);
    end;
    for r := 1{FixedRows} to RowCount - 1 do
    begin
      Cells[0{FixedCols-1}, r] := 'Row ' + IntToStr(r);
      for c := 1{FixedCols} to ColCount - 1 do
      begin
        Inc(box);
        if ddata.TubClientDataSet.Locate('Rackid;Tubid', VarArrayOf([RackId, box]), []) and
           (ddata.TubClientDataSet.FieldByName('id').AsString <> '') then
        begin
          Cells[c, r] := 'Bin ' + IntToStr(box) + ' ' + ddata.TubClientDataSet.FieldByName('id').AsString;
          if (DBEdit17.Text = RackId) and (DBEdit18.Text = IntToStr(box)) then
          begin
            setgridrow := r;
            setgridcol := c;
            //ShowMessage('cell is row ' + IntToStr(r) + ' col ' + IntToStr(c));
          end;
        end else
        begin
          Cells[c, r] := 'Bin ' + IntToStr(box);
        end;
      end;
    end;
    DefaultColWidth := Trunc((Width-56)/ColCount);
    DefaultRowHeight := Trunc((Height-45)/RowCount);
    ColWidths[0] := 56;
  end;
end;

...

if (setgridcol <> -1) and (setgridrow <> -1) then
begin
  //StringGrid1.Refresh;
  ShowMessage(IntToStr(setgridcol) + ' is col ' + IntToStr(setgridrow) + ' is row - try to set cell selected.');
  //StringGrid1.SetFocus;
  ShowMessage('grid focused');
  StringGrid1.Col := setgridcol;
  ShowMessage('col set');
  StringGrid1.Row := setgridrow;
  ShowMessage('row set');
end;

 

Share this post


Link to post

Remy, thank you.  Your code is much cleaner than mine.  Result is the same however.  Grid index out of bounds when attempting to assign the col and row to make the desired cell selected (and thus highlighted in the draw routine).  I'm confused as the variables show what seem to be valid numbers.  For example, a grid with 1 fixed row and 1 fixed col, with 9 string rows and 1 string col - the variables setgridcol is 1 and setgrid row is 2 should (i would think) select the 2nd cell in the grid.  Row, 2, Column 1 in my attached image.  But it throws the error.  Thank you for you assistance, Damon1130669806_Screenshot2022-03-25144023.thumb.jpg.011808e63e2e92301eb65c55f7979cb4.jpg

 StringGrid1.Col := setgridcol;
  ShowMessage('col set');
  StringGrid1.Row := setgridrow;
  ShowMessage('row set');

Share this post


Link to post

Do you understand what the error message tells you? You are using an index i outside the range 0 to Count-1. You can use your debugger to find out which indexing operation is out if bounds. 

 

Debugging by looking at code can be quite hard. Delphi has a powerful debugger for such moments. 

  • Like 1

Share this post


Link to post
4 hours ago, Damon said:

Result is the same however. 

Then it has to be a bug in the Grid, as it should be working given what you have described.  I would suggest turning on "Use Debug DCUs" in the Project Options, and then step into the Grid's source code with the debugger to see exactly what is actually failing, and where it is occurring.

 

On a separate note, have you tried the Grid's Selection property yet?

var r: TGridRect;
r.Left := setgridcol;
r.Top := setgridrow;
r.Right := setgridcol;
r.Bottom := setgridrow;
StringGrid1.Selection := r;
3 hours ago, David Heffernan said:

Do you understand what the error message tells you? You are using an index i outside the range 0 to Count-1.

But, given the screenshot and the values provided, it should not be out of bounds.  That is the issue.

Share this post


Link to post
7 hours ago, Remy Lebeau said:

But, given the screenshot and the values provided, it should not be out of bounds.  That is the issue.

Perhaps the bug is in some other code in the user's program. 

 

One of the recurring themes with questions like this is that the asker often doesn't know how to use, or even realise the value, of the debugger.

 

Re-writing the code for them doesn't help them learn that. 

Share this post


Link to post
7 hours ago, David Heffernan said:

Perhaps the bug is in some other code in the user's program. 

 

One of the recurring themes with questions like this is that the asker often doesn't know how to use, or even realise the value, of the debugger.

 

Re-writing the code for them doesn't help them learn that. 

David, you are in fact correct.  I am terrible at using the debugger even though i have been using delphi since 2.0.  However, my code, albeit quite messy compared to Remy's worked fine and produced exactly the same results.  That piece of code was written by me some 20 years ago for a project i am porting up from D5 and the BDE to D10.x and an SQL backend.  I honestly don't even remember why the variables i decided to use were so cryptic, but they must have made sense to me at the time.  There is no other bug causing it.  I have traced every step to the error in my code, but i do take to heart your advice about debugging and my poor knowledge of the use of it.

Share this post


Link to post
15 hours ago, Remy Lebeau said:

Then it has to be a bug in the Grid, as it should be working given what you have described.  I would suggest turning on "Use Debug DCUs" in the Project Options, and then step into the Grid's source code with the debugger to see exactly what is actually failing, and where it is occurring.

 

On a separate note, have you tried the Grid's Selection property yet?


var r: TGridRect;
r.Left := setgridcol;
r.Top := setgridrow;
r.Right := setgridcol;
r.Bottom := setgridrow;
StringGrid1.Selection := r;

But, given the screenshot and the values provided, it should not be out of bounds.  That is the issue.

Remy, this works.  All i did was comment out the .col and .row assignment and put in this code.  It selects the cell and completes the ondrawcell correctly to color the selected cell.  I guess the other methods just does not work as intended.  ?

Share this post


Link to post

Couple of things that might help with the port is 

 

Quote

ColCount := 1{FixedCols} + ddata.RackClientDataSet.FieldByName('c').AsInteger;
    RowCount := 1{FixedRows} + ddata.RackClientDataSet.FieldByName('r').AsInteger;

That coding would yield where the cursor or record pointer is if a flat file? Renaming Field Name 'c'

to align to what stored in it.   Then Colcount would renamed to align to whats begin returned.  I suspect its layout number.

 

 

  

 

There is a columns count or fields count plus records count for each table. 

 

I think that older codebase should considered in final trim.  Any improvements don't touch vintage code. 

 

If you're using 10.x program the improvements to suit that vintage. Over time d5 vintage code is not used very much.  Good luck comes from months of careful planning and testing of enlightened repairs or good maintenance. 

 

Making a procedure in the DataModule that populates the VCL Control passed to it that can used any form that uses DMxx;   the DMxx would only need VCL.grids added to the uses clause not needing the form calling it adding to uses clause.  

 

DM Public
procedure getDataforSG(aCDS, aCDS2: TClientDataSet;
                                 rangeLeft, rangeRight, rangeTop, rangeBottom: Integer;
                                 aFieldName: string;
                                 //...
                                 aSG: TStringGRid);

...

implementation


procedure TDM.getDataforSG(aCDS, aCDS2: TClientDataSet; rangeLeft, rangeRight, rangeTop, rangeBottom: Integer;
  aFieldName: string; aSG: TStringGRid);
var
  I,J: integer;
  RackID: Integer;
  TubIDcol: Integer;//Variant;
  csvfieldNames: string;
  fishName: string;
begin
  aSG.Rows[0].CommaText := 'row'+ aFieldName;
  if rangeRight > CDS.FieldCount then
      rangeRight := CDS.FieldCount;
  if rangeBottom > CDS.RecordCount then
      rangeBottom:= CDS.recordCount;
  aSG.ColCount := rangeRight-rangeLeft;
  aSG.RowCount := -rangeBottom-rangeTop;

  //To find what c is about
  //pass in aFieldName := 'c';
  for I := rangeTop to rangeBottom do
    begin
      aCDS.RecNo := I;
      fishName := ACDS.FieldByName(aFieldName).AsString;
      ASG.Rows[I].CommaText := aFieldName + ',' + fishName;
    end;

  //build SL header
  for I := rangeLeft to rangeRight do
  begin

    csvfieldNames := csvfieldNames + CDS.Fields[I].Name + ','
  end;
  ASG.Rows[0].CommaText := ('Row,' + csvfieldNames);

  //Put your tubCDS code here

 

Insert into form 

procedure Tmain.buildrackClick(Sender: TObject);
const 
  MagicNumberUp = 1;
  MagicNumberDown = -1;// would need inserted to adjust SG row offset 

var
  c, r, box : Integer;
  RackId : string;
begin
  //ShowMessage('draw grid');
  //setgridrow := -1;
  //setgridcol := -1;
  //box := 0;
  //RackId := ListBox1.Items[ListBox1.ItemIndex];
  RackId := 'c';
  getDataforSG(ffdata,tubdata,0 +MagicNumberUp,50000,0+MagicNumberUp,50000,RackID,StringGrid1);
  //(**   comment rest out for now **)

 

 

 

 

 

 

 

 

 

 

      

 

 

 

 

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×