String Grid Demonstration

Program StringGridDemo displays an array of records in a string grid. When you edit, add and delete records the procedures change the underlying array accordingly. You save the records to a file and open the file the next time you use the program. The first time that you run the program, if you do not have a data file, the program will create one from a hard-coded array. If you click on the heading of the Forename or Surname column you sort the records based on the data in that column. You could write procedures that sort the records based on the data in the Theory and Practical columns. (The inbuilt sort procedure works for strings).

Program StringGridDemo in action

Program StringGridDemo in action

The essential code of the files needed for program StringGridDemo follows. You can download the code of the three files in form_stringgrid_demo.zip

uStringGridDemo

unit uStringGridDemo;

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids,
  StdCtrls;

type
  TfrmGrid = class(TForm)
    btnNewRecord: TButton;
    btnDelete: TButton;
    sgMarks: TStringGrid;
    procedure btnDeleteClick(Sender: TObject);
    procedure btnNewRecordClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure sgMarksEditingDone(Sender: TObject);
    procedure sgMarksHeaderClick(Sender: TObject; IsColumn: Boolean;
                                 Index: Integer);
  end; 

var
  frmGrid: TfrmGrid;

implementation

{$R *.lfm}

type
  TStudent = record
    Forename, Surname : string[15];
    TheoryMark, PracticalMark : integer;
  end;

const
  MAX = 10;
  RESULTS_FILE = 'results.txt';

var
  Students : array[1 .. MAX] of TStudent;
  StudentFile : file of TStudent;
  NumOfRecords : integer;

procedure Save;
var
  i : integer;
begin
  assignFile(StudentFile, RESULTS_FILE);
  rewrite(StudentFile);
  for i := 1 to NumOfRecords do
    begin
      write(StudentFile, Students[i]);
    end;
  closeFile(StudentFile);
end;

procedure PopulateArray;
begin
  if FileExists(RESULTS_FILE) then
    begin
      assignFile(StudentFile, RESULTS_FILE);
      reset(StudentFile);
      NumOfRecords := 0;
      while not eof(StudentFile) do
        begin
          inc(NumOfRecords);
          read(StudentFile, Students[NumOfRecords]);
        end;
      closeFile(StudentFile);
    end
  else
    begin
      with Students[1] do
        begin
          Forename := 'Jo';
          Surname :='Wood';
          TheoryMark := 55;
          PracticalMark := 66;
        end;
      with Students[2] do
        begin
          Forename := 'John';
          Surname := 'Bode';
          TheoryMark := 73;
          PracticalMark := 58;
        end;
      with Students[3] do
        begin
          Forename := 'Kapil';
          Surname := 'Shah';
          TheoryMark := 59;
          PracticalMark := 58;
        end;
      NumOfRecords := 3;
    end;
end;

procedure FillGrid;
var
  i : integer;
begin
  with frmGrid.sgMarks do
    begin
      RowCount := NumOfRecords + 1; //Top row is for headings
      for i := 1 to NumOfRecords do
        begin
          Cells[0, i] := intToStr(i);
          Cells[1, i] := Students[i].Surname;
          Cells[2, i] := Students[i].Forename;
          Cells[3, i] := intToStr(Students[i].TheoryMark);
          Cells[4, i] := intToStr(Students[i].PracticalMark);
        end;
     end;
end;

procedure GridToArray;
var
  i : integer;
begin
  with frmGrid.sgMarks do
    begin
      for i := 1 to NumOfRecords do
        begin
          Students[i].Surname := Cells[1, i];
          Students[i].Forename := Cells[2, i];
          Students[i].TheoryMark := strToInt(Cells[3, i]);
          Students[i].PracticalMark := strToInt(Cells[4, i]);
        end;
     end;
end;

procedure TfrmGrid.FormCreate(Sender: TObject);
begin
  PopulateArray;
  FillGrid;
end;

procedure TfrmGrid.sgMarksEditingDone(Sender: TObject);
begin
  with sgMarks do
    begin
      if sgMarks.Cells[1, Row] <> '' then
        case Col of
          1 : Students[Row].Surname:= sgMarks.Cells[1, Row];
          2 : Students[Row].Forename:= sgMarks.Cells[2, Row];
          3 : Students[Row].TheoryMark := strToInt(sgMarks.Cells[3, Row]);
          4 : Students[Row].PracticalMark := strToInt(sgMarks.Cells[4, Row]);
        end;
    end;
  save;
end;

procedure TfrmGrid.sgMarksHeaderClick(Sender: TObject; IsColumn: Boolean;
                                    Index: Integer);
var
  i : integer;
begin
  with sgMarks do
    if IsColumn and ((Index = 1) or (Index = 2)) then
      begin
        sortColRow(true, Index);
        for i := 1 to NumOfRecords do
          Cells[0, i] := intToStr(i);
        GridToArray;
      end;
end;

procedure TfrmGrid.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  Save;
end;

procedure TfrmGrid.btnNewRecordClick(Sender: TObject);
begin
  inc(NumOfRecords);
  with sgMarks do
    begin
      RowCount := RowCount + 1;
      Cells[0, RowCount - 1] := intToStr(RowCount - 1);
      Row := RowCount - 1;
    end;
end;

procedure TfrmGrid.btnDeleteClick(Sender: TObject);
var
  i, j : integer;
begin
  //Shuffle all records after deleted record down one position
  for i := sgMarks.Row to sgMarks.RowCount - 2 do  //-2 because we stop one row before
                                                   //the last and RowCount includes heading
    for j := 0 to 4 do
      sgMarks.Cells[j, i] := sgMarks.Cells[j, i + 1];
  sgMarks.RowCount := sgMarks.RowCount - 1;
  dec(NumOfRecords);
  for i := 1 to NumOfRecords do
    sgMarks.Cells[0, i] := intToStr(i);
  GridToArray;
end;

end.
  

uStringGridDemo.lfm

object frmGrid: TfrmGrid
  Left = 445
  Height = 253
  Top = 205
  Width = 404
  Caption = 'Results'
  ClientHeight = 253
  ClientWidth = 404
  OnClose = FormClose
  OnCreate = FormCreate
  LCLVersion = '0.9.30'
  object sgMarks: TStringGrid
    Left = 8
    Height = 188
    Top = 8
    Width = 381
    Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing, goSmoothScroll]
    RowCount = 4
    TabOrder = 0
    OnEditingDone = sgMarksEditingDone
    OnHeaderClick = sgMarksHeaderClick
    ColWidths = (
      84
      68
      73
      64
      64
    )
    Cells = (
      5
      0
      0
      'Record No'
      1
      0
      'Surname'
      2
      0
      'Forename'
      3
      0
      'Theory'
      4
      0
      'Practical'
    )
  end
  object btnNewRecord: TButton
    Left = 32
    Height = 25
    Top = 208
    Width = 98
    Caption = 'New Record'
    OnClick = btnNewRecordClick
    TabOrder = 1
  end
  object btnDelete: TButton
    Left = 240
    Height = 25
    Top = 208
    Width = 112
    Caption = 'Delete Record'
    OnClick = btnDeleteClick
    TabOrder = 2
  end
end  

StringGridDemo.lpr

program StringGridDemo;
uses
  Interfaces, Forms, uStringGridDemo;
begin
  Application.Initialize;
  Application.CreateForm(TfrmGrid, frmGrid);
  Application.Run;
end. 
  

We describe ways of validating the text entered into a string grid in the next section.

Programming - a skill for life!

How to use a string grid to display and edit data