We want to create a report were table in word document will be poplulated at runtime using merge fileds. All the examples we have seen, columns in word table are fixed with pre defined merge fields.
We would like to populate word table with dynamic columns and rows from the datatable at runtime since number of columns are not known at design time.
Can you give us an sample code on how to do this? Also we would like to have first row in word table as header row which repeats across pages.
Thanks
Hi Nik,
Thanks for your request. You can use Documentbuilder to build table with random number of columns. For example, see the following code:
public void Test165()
{
// Get dummy datasource (data table with random number of rows)
DataTable data = GetDataTable();
// Open or create document and create DocumentBuilder
Document doc = new Document();
// Document builder will be needed to build table in the document
DocumentBuilder builder = new DocumentBuilder(doc);
// if you use template, you can move documentBuilder cursor to any location
// Using MoveToXXX methods, MoveToBookmark for instance
// Customize builder properties
builder.CellFormat.Borders.LineStyle = LineStyle.Single;
builder.CellFormat.Borders.LineWidth = 1;
builder.Font.Name = "Arial";
builder.Font.Size = 10;
// Start building table
builder.StartTable();
// build header row
builder.CellFormat.Shading.BackgroundPatternColor = Color.LightGray;
builder.Font.Bold = true;
builder.Font.Italic = true;
builder.RowFormat.HeadingFormat = true;
foreach (DataColumn col in data.Columns)
{
builder.InsertCell();
builder.Write(col.ColumnName);
}
builder.EndRow();
// Starting from here there is two aproach.
// First build entire table using documentBuilder
// another build one row with mergefields and use mail merge to fill table with data
// Here we will use the second aproach
// Build entire table
builder.CellFormat.Shading.BackgroundPatternColor = Color.FromArgb(255, 255, 255, 255);
builder.Font.Bold = false;
builder.Font.Italic = false;
builder.RowFormat.HeadingFormat = false;
foreach (DataColumn col in data.Columns)
{
builder.InsertCell();
// If it is first column we should insert tableStart mergefield
if (col.Equals(data.Columns[0]))
builder.InsertField(String.Format("MERGEFIELD \"TableStart:{0}\"", data.TableName), "");
// Insert mergefield
builder.InsertField(String.Format("MERGEFIELD \"{0}\"", col.ColumnName), "");
// If column is last we should insert TableEnd
if (col.Equals(data.Columns[data.Columns.Count - 1]))
builder.InsertField(String.Format("MERGEFIELD \"TableEnd:{0}\"", data.TableName), "");
}
builder.EndRow();
builder.EndTable();
// Now we ca execute mail merge
doc.MailMerge.ExecuteWithRegions(data);
// Save output document
doc.Save(@"Test165\out.doc");
}
private DataTable GetDataTable()
{
// Create dummy datasource that contains randome number of culumns
Random rnd = new Random();
DataTable data = new DataTable("data");
int colCount = rnd.Next(1, 10);
// Add columns
for (int colIdx = 0; colIdx < colCount; colIdx++)
{
data.Columns.Add(string.Format("Column_{0}", colIdx));
}
// Add random number of rows
int rowsCount = rnd.Next(1, 100);
for (int rowIdx = 0; rowIdx < rowsCount; rowIdx++)
{
DataRow row = data.NewRow();
foreach (DataColumn col in data.Columns)
{
row[col] = string.Format("Column:{0}; Row:{1}", col.ColumnName, rowIdx);
}
data.Rows.Add(row);
}
return data;
}
Hope this helps.
Best regards.
Thanks for the quick response alexey
Our report contains several tables. Some are predefined (hence, we can format them manually in word), and some are dynamically created (as mentioned in the above question). How can we carry over the same formatting from the fixed tables to the dynamically created ones?
Hi
Thanks for your inquiry. I think you can achieve this using cloning. Concept is following:
- Create table in MS Word that has one column and two rows. This will be template. Customize the table. In addition, insert bookmark at the firs cell (this will be table identifier).
- In the code, move document builder cursor and clone cells.
For example, see the following code and attached template:
public void Test165()
{
// Get dummy datasource (data table with random number of rows)
DataTable data = GetDataTable();
// Open or create document and create DocumentBuilder
Document doc = new Document(@"Test165\in.doc");
// Document builder will be needed to build table in the document
DocumentBuilder builder = new DocumentBuilder(doc);
// move documentBuilder cursor to the bookmark inside table
builder.MoveToBookmark("myTable");
// Get table
Table myTable = (Table)builder.CurrentNode.GetAncestor(NodeType.Table);
// build header row
foreach (DataColumn col in data.Columns)
{
// Clone first cell of first row to build header of the table
Cell hCell = (Cell)myTable.FirstRow.FirstCell.Clone(true);
// Insert cell into the first row
myTable.FirstRow.AppendChild(hCell);
// Move document builder cursor to the cell
builder.MoveTo(hCell.FirstParagraph);
// Insert text
builder.Write(col.ColumnName);
}
// Build entire table
foreach (DataColumn col in data.Columns)
{
// Clone first cell of the second row to build header of the table
Cell bCell = (Cell)myTable.Rows[1].FirstCell.Clone(true);
// Insert cell into the second row
myTable.Rows[1].AppendChild(bCell);
// Move document builder cursor to the cell
builder.MoveTo(bCell.FirstParagraph);
// If it is first column we should insert tableStart mergefield
if (col.Equals(data.Columns[0]))
builder.InsertField(String.Format("MERGEFIELD \"TableStart:{0}\"", data.TableName), "");
// Insert mergefield
builder.InsertField(String.Format("MERGEFIELD \"{0}\"", col.ColumnName), "");
// If column is last we should insert TableEnd
if (col.Equals(data.Columns[data.Columns.Count - 1]))
builder.InsertField(String.Format("MERGEFIELD \"TableEnd:{0}\"", data.TableName), "");
}
// Here we remove first empty column
myTable.Rows[0].Cells[0].Remove();
myTable.Rows[1].Cells[0].Remove();
// Now we ca execute mail merge
doc.MailMerge.ExecuteWithRegions(data);
// Save output document
doc.Save(@"Test165\out.doc");
}
Hope this helps.
Best regards.
Very use full code.
I want to display the multiple headers for the table(by merging two coulmns n rows …please see the attached)
Please tell me ho wto modify the above code to display multple headers for my dynamic table.and also is there anyway we can remove the header and report only rows and columns??
Hi Anju,
Thanks for your inquiry. Please read following documentation link for your kind reference.
https://docs.aspose.com/words/net/working-with-columns-and-rows/
The code shared in this thread generate table dynamically without any cell merge and do mail merge. The code generates table with only two rows. You can insert code shared here before for loop in above code.
// build header row
builder.RowFormat.HeadingFormat = true;
…
…
// build header row
// insert code here…
foreach (DataColumn col in data.Columns)
{
builder.InsertCell();
builder.Write(col.ColumnName);
}
builder.EndRow();
Please manually create your expected Word document using Microsoft Word and attach it here for our reference. We will investigate as to how you want your final Word output be generated like. We will then provide you more information on this along with code.
Please also share some more detail about your scenario what exact you want to achieve by using Aspose.Words.