5 Minute Snack: Creating columns with calculated field values on demand with TTMSFNCGrid

As mentioned, I am in the process of preparing a video tutorial about FNC controls. The next video will be about TTMSFNCGrid, a bindable and datasource capable datagrid that will behave the same way in any framework and any platform.

The amount of properties, methods and events is immense and thus you can only get to know the control by using it in your projects. One of these projects involved dislaying additional information for the user that is based on the columns displayed in the grid.

I will simplify my actual example as such:

In the grid we allow the user to enter a start date and an end date for a project in the first two columns of each row. The third column will display the number of days between these dates. Pretty simple task, but no grid in this would has predefined calculations like this.

We have several approaches in Delphi. One approach would be to create an TFDMemTable with two fields and an additional calculated field for the number of days. This dataset can then be bound to the grid.

However, this is a simple example and for a real-life project we might not want to change our datasets for a visual finesse. 

Thus, we simply want to use the grid control to display that info for us, but we are not interested in persisting this information or use it anywhere else. 

After having created a VCL Forms Application I dropped a TTMSFNCGrid on the form and defined the number of Columns (ColumnCount ) and Rows (RowCount ) using their respective properties. I also set FixedColumns  and FixedRows  to 1 . Set DefaultColumnWidth  to 120  and DefaultRowHeight  to 24 . 

In order to add rows, I add a button of type TTMSFNCButton .

The form should look something like this:

You might add anchors and other eye candy as you please.

Next, we have to allow for the user to add rows to the grid. For this, we need to implement the GridCanAppendRow-event:

procedure TForm1.GridCanAppendRow(Sender: TObject; ARow: Integer;
  var Allow: Boolean);
  // allow that rows can be added
  Allow := True;

Pretty simple, as we want to allow a row to be added without any restrictions. Of course, you could limit the number of total rows by stating:

Allow := Grid.RowCount < 22;

This will allow for 20 user-rows to be added. In case you ever add a footer you need to consider that row as well.

The button does the actual adding of the row:

procedure TForm1.btAddClick(Sender: TObject);
  // append a row
  Grid.RowCount := Grid.RowCount +1;

Unlike the VCL a change of rows does not delete the content of the grid. The FNC grid is pretty smart and notices that you simply want to add a row and keeps the data in place.

If you ran the example right now, you would already be able to enter data and add rows. However, there is absolutely no user comfort entering the data. Also the difference in days is not being calculated.

Double-click the grid and amend some properties for the columns:

We select the etDatePicker  for the columns the user enters the dates into. Furthermore, we can set ReadOnly to true  for the calculated column.

In order for the user to be able to navigate the grid nicely we set some additional keyboard options. We want to the user be able to press enter to go to the next column and add a row in case the last column of the row has been reached. Furthermore, we want the user to be able to use Tab to navigate between the columns:

For this we have the properties Grid.Keyboard.EnterKeyHandling  and Grid.Keyboard.TabKeyHandling  as seen in the screenshot above.

The only thing missing is the calculation. The grid offers the event OnGetCellData :

procedure TForm1.GridGetCellData(Sender: TObject; ACol, ARow: Integer;
  var CellString: string);
  e : TDateTime;

  // only for column 3
  // not for fixed row
  if ( ARow > 0 ) and (ACol = 3) then
    CellString := '';

    // if date is valid proceed
    if TryStrToDate( Grid.Cells[ 1, ARow ], s ) then
      // if date is valid, calculate difference
      if TryStrToDate( Grid.Cells[ 2, ARow ], e ) then
        CellString := IntToStr( DaysBetween( e, s ) );


The implementation is pretty straight forward. If there is no valid data, the column stays empty. Otherwise, we return the number of days in the provided variable named CellString .

Running the example yields a very comfortable user experience with the desired result:


Tags: , , , ,
One comment on “5 Minute Snack: Creating columns with calculated field values on demand with TTMSFNCGrid
  1. ziad ibrahim allaghi says:

    thank you good idea for code