Best Practices for Mail Merge and Form Field Processing in ASP.NET Core C# Applications
This article provides best practices for mail merge and form field processing in ASP.NET Core C# applications using TX Text Control .NET Server. It shows how to use the Document Editor to create templates and how to merge data into those templates using the MailMerge class.

TX Text Control provides all the necessary components used in a typical form field processing workflow. From designing the template using a customizable and programmable document editor, to merging data into the template, to deploying the form for users to fill in form fields. After the document is completed, the final document is created as a PDF and the completed form field values are extracted for processing.
This tutorial describes the best-practice sample project that contains all the necessary process steps and classes to integrate such a document processing workflow into your own applications.
It shows the following steps:
- Designing the template using the TX Text Control Document Editor
- Merging data into the template
- Deploying the form for users to fill in form fields
- Extracting the form field values from the completed document
- Creating the final document as a PDF with flattened form fields
The Sample Project
The sample project is an ASP.NET Core MVC C# application that uses the TX Text Control Document Editor to design the template. The template is then merged with data and deployed for users to fill in form fields using the Document Viewer. After the document is completed, the final document is created as a PDF and the completed form field values are extracted for processing.
Designing the Template
The template is designed using the TX Text Control Document Editor. The editor is a fully programmable and customizable WYSIWYG word processing editor that can be integrated into any application. The editor is used to design the template with merge fields and form fields that are later filled in by users.
After starting the sample project, the dashboard shows two options: Create Template and Merge and Deploy. Click on Create Template to start the Document Editor.
A view containing the TX Text Control Document Editor will open and you can begin designing your template. A sample JSON data object is loaded into the editor to demonstrate how merge fields can be used to merge data into the template. This data source is also used to merge the template to generate the document. To load a pre-designed template that contains merge fields and form fields that match the data source, click Load Pre-Designed Template.
Saving the Template
If you are happy with the template click on Save Template and close the view by clicking Close.
Let's take a look at what happens in the code in this step. The SaveDocument JavaScript function saves the document and sends it to the SaveTemplate controller endpoint.
function saveDocument() {
TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, document => {
dirtyFlag = false;
// send document.data to endpoint using ajax
$.ajax({
type: "POST",
url: "/Document/SaveTemplate?templateName=@templateName",
data: JSON.stringify(document.data),
contentType: "application/json",
dataType: "json",
success: function (response) {
toggleSaveButtonEnabled();
},
error: function (response) {
console.log(response);
}
});
});
}
The endpoint saves the document to the server and returns the generated document filename. This filename is used to load the document in the Document Viewer later.
[HttpPost]
public IActionResult SaveTemplate([FromBody] string document, [FromQuery] string templateName)
{
if (string.IsNullOrEmpty(document) || string.IsNullOrEmpty(templateName))
{
return BadRequest("Invalid input: Document and template name must be provided.");
}
try
{
// Convert base64 string to byte array
byte[] bytes = Convert.FromBase64String(document);
// Ensure the directory exists
string directoryPath = Path.Combine("Templates");
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
// Safely combine the path and template name
string filePath = Path.Combine(directoryPath, templateName);
// Save the document to a file
System.IO.File.WriteAllBytes(filePath, bytes);
// Return JSON with the file name
return Json(new { templateName = templateName });
}
catch (FormatException)
{
return BadRequest("Invalid base64 string.");
}
catch (IOException ex)
{
return StatusCode(500, "Error saving the file.");
}
catch (Exception ex)
{
return StatusCode(500, "An unexpected error occurred.");
}
}
Merging Data into the Template
After saving the template, the dashboard shows the Merge and Deploy option. Click on this option to merge the template with the sample data and deploy the document for users to fill in form fields.
Select the template you created from the drop-down list and click Merge and Load.
This is where the MergeTemplate endpoint is called with the name of the template.
[HttpGet]
public IActionResult MergeTemplate([FromQuery] string templateName)
{
if (string.IsNullOrEmpty(templateName))
{
return BadRequest("Template name must be provided.");
}
try
{
// Safely combine the path and template name
string templatePath = Path.Combine("Templates", templateName);
string dataPath = Path.Combine("Data", "data.json");
if (!System.IO.File.Exists(templatePath))
{
return NotFound("Template not found.");
}
if (!System.IO.File.Exists(dataPath))
{
return NotFound("Data file not found.");
}
// Read the template and data files
byte[] templateBytes = System.IO.File.ReadAllBytes(templatePath);
string jsonData = System.IO.File.ReadAllText(dataPath);
using (ServerTextControl tx = new ServerTextControl())
{
// Load the template
tx.Create();
tx.Load(templateBytes, BinaryStreamType.InternalUnicodeFormat);
MailMerge merge = new MailMerge
{
TextComponent = tx,
FormFieldMergeType = FormFieldMergeType.Preselect
};
// Merge the template with JSON data
merge.MergeJsonData(jsonData);
// Save the merged document to a byte array
byte[] document;
tx.Save(out document, BinaryStreamType.InternalUnicodeFormat);
// Convert byte array to base64 string
string documentBase64 = Convert.ToBase64String(document);
// Return the document as a JSON object
return Json(new { document = documentBase64 });
}
}
catch (IOException ex)
{
return StatusCode(500, "Error reading files or saving the document.");
}
catch (Exception ex)
{
return StatusCode(500, "An unexpected error occurred.");
}
}
The endpoint merges the template with the sample data and returns the merged document. This document is loaded into the Document Viewer for users to fill in form fields.
Finalizing the Document
Fill in some of the form fields and click Submit to send the document back to the server. The asynchronous JavaScript function finalizeDocument saves the document by including the form field values. The returned document is then sent to the Finalize controller method, which generates and returns a PDF document that is then offered for download and loaded into the viewer.
async function finalizeDocument() {
// export the document with the form fields
const saveSettings = { mergeFormFields: true, embedAnnotations: false };
const result = await TXDocumentViewer.saveDocument(
TXDocumentViewer.StreamType.InternalUnicodeFormat,
saveSettings);
const base64 = await result.base64();
// finalize the document on the server
$.ajax({
type: "POST",
url: "/Document/Finalize?templateName=" + templateName,
data: JSON.stringify(base64),
contentType: "application/json",
dataType: "json",
success: function (data) {
var link = document.createElement('a');
link.href = 'data:application/pdf;base64,' + data.document;
link.download = 'document.pdf';
link.click();
var documentLoadSettings = {
pdfjs: {
workerSourcePath: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.mjs',
librarySourcePath: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.min.mjs'
}
}
// load the document into the viewer
TXDocumentViewer.loadDocument(data.document, "document.pdf", null, documentLoadSettings);
}
});
}
The Finalize method uses the Server
[HttpPost]
public IActionResult Finalize([FromBody] string document, [FromQuery] string templateName)
{
if (string.IsNullOrEmpty(document) || string.IsNullOrEmpty(templateName))
{
return BadRequest("Document and template name must be provided.");
}
try
{
using (ServerTextControl tx = new ServerTextControl())
{
// Load the document
tx.Create();
byte[] bytes = Convert.FromBase64String(document);
tx.Load(bytes, BinaryStreamType.InternalUnicodeFormat);
// Convert form fields to JSON
string jsonData = FormFieldHelper.FormFieldsToJson(tx);
// Get filename from templateName without extension
string fileName = Path.GetFileNameWithoutExtension(templateName);
string dataPath = Path.Combine("Data", fileName + ".json");
// Ensure the directory exists
string directoryPath = Path.GetDirectoryName(dataPath);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
// Save JSON data to file
System.IO.File.WriteAllText(dataPath, jsonData);
// Flatten form fields
FormFieldHelper.FlattenFormFields(tx);
// Save the document to a byte array
byte[] finalDocument;
tx.Save(out finalDocument, BinaryStreamType.AdobePDF);
// Convert byte array to base64 string
string finalDocumentBase64 = Convert.ToBase64String(finalDocument);
// Return the document as a JSON object
return Json(new { document = finalDocumentBase64 });
}
}
catch (FormatException)
{
return BadRequest("Invalid base64 string.");
}
catch (IOException ex)
{
return StatusCode(500, "Error processing the document.");
}
catch (Exception ex)
{
return StatusCode(500, "An unexpected error occurred.");
}
}
In the following screenshot, you can see the flattened PDF file loaded into the Document Viewer and the downloaded PDF file.
Conclusion
This tutorial showed how to create a form field processing workflow using TX Text Control. The sample project demonstrates how to design a template using the TX Text Control Document Editor, merge data into the template, deploy the document for users to fill in form fields, extract the form field values from the completed document, and create the final document as a PDF with flattened form fields.
The sample project is available on GitHub and can be downloaded and tested. The project contains all the necessary process steps and classes to integrate such a document processing workflow into your own applications.
Download and Fork This Sample on GitHub
We proudly host our sample code on github.com/TextControl.
Please fork and contribute.
Requirements for this sample
- TX Text Control .NET Server
- Visual Studio 2022
ASP.NET
Integrate document processing into your applications to create documents such as PDFs and MS Word documents, including client-side document editing, viewing, and electronic signatures.
- Angular
- Blazor
- React
- JavaScript
- ASP.NET MVC, ASP.NET Core, and WebForms
Related Posts
Advantages of Flow Type Layout Reporting vs. Banded Reporting or PDF…
This article shows the advantages of flow type layout reporting compared to banded reporting or populating PDF placeholders in .NET C#. It explains the differences and the benefits of using a flow…
Use MailMerge in .NET on Linux to Generate Pixel-Perfect PDFs from DOCX…
This article explores how to use the TX Text Control MailMerge feature in .NET applications on Linux to generate pixel-perfect PDFs from DOCX templates. This powerful combination enables…
Generating Dynamic NDAs Using TX Text Control MailMerge in C# .NET
This article demonstrates how to generate dynamic NDAs using TX Text Control MailMerge in C# .NET. It covers the process of creating a template, binding data, and generating the final document.
Designing a Maintainable PDF Generation Web API in ASP.NET Core (Linux) C#…
This article shows how to create a PDF generation Web API in ASP.NET Core on Linux using TX Text Control .NET Server. The clean architecture is used to create a maintainable and testable solution.
Manipulating Table Cells During the MailMerge Process in .NET C#
This article shows how to manipulate table cells during the mail merge process in .NET C#. The FieldMerged event can be used to manipulate the table cells after they are merged.