A dynamic table is realized by a TParagraph with the type table which includes sub paragraphs (actually rows) in the list which start with the property "TemplatePar".


Such  template paragraphs are used to create, re-create, fill and format the data cells. The template rows are organized in sequential order using the NextPar reference of each TParagraph. Each row contains child paragraphs which represent the cells. Groups are not built by recursion but by using special separator paragraphs in this list of rows.


The array property template_rows makes it possible to access the template rows, i.e. to add some additional information to the header rows.

The index in the array is data row offset, -1 will access a header and -2 a footer row.


Templates using this dynamic tables can only be saved and loaded in "WPT" format. In any other format the template paragraphs will be removed and the table will appear as a regular table.


To create a dynamic table structure a TWPDataSetAdapter component is required. It contains the necessary functions to know which columns should be created and which data should be included. The TWPDataSetAdapter is referenced by the TWPRichTexts WPDataSetAdapter property. This references is necessary, because the editor can access this dataset adapter when required.


If you have a TWPDataSetAdapter on your form which defines 2 data source links:


object WPDataSetAdapter1: TWPDataSetAdapter

 DataSourceLinks = <


     Name = 'MASTERTABLE'

     DisplayName = 'the main table'

     DisplayDescription = 'the main table'

     UIDFieldName = 'MID'

     DataSource = DSMaster

     DynamicDataSource = False



     Name = 'CLIENTTABLE'

     DisplayName = 'Sub Data'

     UIDFieldName = 'CID'

     MasterField = 'LinkMID'

     DataSource = DSClient

     DynamicDataSource = False

     OnDataSourceInitialize = WPDataSetAdapter1DataSourceLinks1DataSourceInitialize


 Left = 308

 Top = 443



This code can be used to insert a table into the document which uses the data retrieved by the adapter:


var grid : TParagraph;


  grid := WPRichText1.AddDynamicGrid('MASTERTABLE', nil, nil, WPCentimeterToTwips(0.6));

  grid := WPRichText1.AddDynamicGrid( 'CLIENTTABLE', nil, grid, WPCentimeterToTwips(0.3),

  [ wptblActivateBorders,wptblCreateHeaderRow, wptblCreateIndentColumn, wptblCreateFooterRow ], nil, 1 );



Here two tables are created with one inside of the other. This does not mean that nested tables are created, but that the inner table uses data which is selected for each row of the outer tables. (Master-Client relationship)


After creating the template it can be filled with data


grid := WPRichText1.FindParagraph('MASTERTABLE');

if grid<>nil then grid.GridUpdate([wpInitDataCells,wpLoadDataCells], nil)

WPRichText1.ReformatAll(true, true);



After the creation of the table it is possible to edit the table. i.e. the columns can be resized or special colors can be assigned.

Since the cell properties are stored in the template, such changes will survive a recreation of the data using GridUpdate. Further more a change to one cell will change all other cells automatically which are based on the same template.


The concept is prepared to not load all data on the initial call to GridUpdate but when the data is required for display. This mode is used when  the flag wpLoadDataCell was not provided. This mode however requires that the DataSetAdapter can access any of the data rows randomly using a unique ID.