The Mail
╰ DocumentServer Namespace
╰ MailMerge Class
The MailMerge class is a .NET component that can be used to effortlessly merge template documents with database content in .NET projects, such as ASP.NET web applications, web services or Windows services. class in TX Text Control is a robust library designed to automate document creation through the process of merging data into templates. It serves as a bridge between structured data (e.g., from databases, JSON, or XML) and dynamic document generation, making it invaluable for applications that require automated document workflows.
At its core, the MailMerge class simplifies the complex task of creating professional, data-driven documents by allowing developers to easily merge fields in templates with data sources. Templates are designed using the TX Text Control word processing interface, with merge fields representing dynamic content.
Merge Blocks
Merge blocks are a key concept in TX Text Control's MailMerge class, which allows for the dynamic generation of structured, repeatable content in documents. Merge blocks allow developers to effectively handle repeating data structures such as lists, tables, or nested regions in templates.
At a high level, a merge block is a defined section within a template that corresponds to a collection or table of data. During the merge process, MailMerge iterates through the data source, dynamically creating content for each record. These merge blocks can be conditionally rendered, filtered, and sorted using the out-of-the-box functionality of the MailMerge class.
Code-Level Manipulation
But the MailMerge class also allows very detailed, code-level manipulation during the merge process. This includes handling merge events, customizing the merge process, and programmatically controlling the output of the merge operation. This level of control is essential for complex document generation scenarios that require fine-grained control over the merge process.
This article describes how to use the Field
╰ DocumentServer Namespace
╰ MailMerge Class
╰ FieldMerged Event
Occurs when a field has been merged. event to manipulate a table cell that is returned by the event when the merged field is inside a table cell. For this example, we will use a very simple template consisting of two merge fields and a simple repeating block highlighted in red.
To merge the template, we will use the following simplified JSON data.
[ | |
{ | |
"invoice-id": "1", | |
"invoice-date": "2019-01-01", | |
"line-items": [ | |
{ | |
"product-id": "1", | |
"quantity": "1", | |
"unit-price": "8" | |
}, | |
{ | |
"product-id": "2", | |
"quantity": "2", | |
"unit-price": "200" | |
}, | |
{ | |
"product-id": "3", | |
"quantity": "1", | |
"unit-price": "100" | |
}, | |
{ | |
"product-id": "4", | |
"quantity": "1", | |
"unit-price": "3" | |
} | |
] | |
} | |
] |
In order to merge this template, the following code will be used:
textControl1.Load("template.tx", TXTextControl.StreamType.InternalUnicodeFormat); | |
MailMerge mailMerge = new MailMerge(); | |
mailMerge.TextComponent = textControl1; | |
string jsonData = File.ReadAllText("data.json"); | |
mailMerge.MergeJsonData(jsonData); |
This results in the following merged document:
FieldMerged Event
Now let's attach the FieldMerged event to manipulate the merge process.
textControl1.Load("template.tx", TXTextControl.StreamType.InternalUnicodeFormat); | |
MailMerge mailMerge = new MailMerge(); | |
mailMerge.TextComponent = textControl1; | |
mailMerge.FieldMerged += MailMerge_FieldMerged; | |
string jsonData = File.ReadAllText("data.json"); | |
mailMerge.MergeJsonData(jsonData); |
Each time a merge field is merged (successfully or not), this event is invoked. A Table
╰ DocumentServer Namespace
╰ MailMerge.FieldMergedEventArgs Class
╰ TableCell Property
If the merge field is inside of a table, this property returns the containing table cell as a TXTextControl.TableCell instance or null otherwise. is returned if the field is located within a table cell.
In this event, we check that the TableCell property is not null. If it is not null, we set the background color of the cell.
private void MailMerge_FieldMerged(object sender, MailMerge.FieldMergedEventArgs e) | |
{ | |
if (e.TableCell != null) | |
{ | |
e.TableCell.CellFormat.BackColor = Color.Red; | |
e.TableCell.CellFormat.BottomBorderWidth = 60; | |
e.TableCell.CellFormat.BottomBorderColor = Color.Blue; | |
} | |
} |
As a result, all merged cells are colored during the merge process.
Dynamic Cell Colors
Let's make this process dynamic by adding some logic. The CellFilterInstructions class is used to compare a value with a given value to return a specific color.
using System; | |
using System.Drawing; | |
using System.Linq; | |
public class CellFilterInstructions | |
{ | |
public double? CompareValue { get; set; } = null; | |
public RelationalOperator? Operator { get; set; } = null; | |
public Color TrueColor { get; set; } = Color.White; | |
public Color FalseColor { get; set; } = Color.White; | |
public enum RelationalOperator | |
{ | |
Equals = 0, | |
NotEqual, | |
LessThan, | |
GreaterThan, | |
} | |
// evaluates the instruction and returns the proper color | |
public Color? GetColor(string value) | |
{ | |
if (Double.TryParse(ParseToNumber(value), out double dValue) == true) | |
{ | |
switch (Operator) | |
{ | |
case RelationalOperator.Equals: | |
return (dValue == CompareValue ? TrueColor : FalseColor); | |
case RelationalOperator.NotEqual: | |
return (dValue != CompareValue ? TrueColor : FalseColor); | |
case RelationalOperator.GreaterThan: | |
return (dValue > CompareValue ? TrueColor : FalseColor); | |
case RelationalOperator.LessThan: | |
return (dValue < CompareValue ? TrueColor : FalseColor); | |
default: | |
return null; | |
} | |
} | |
else | |
return null; | |
} | |
private string ParseToNumber(string text) | |
{ | |
var numericChars = "0123456789,.".ToCharArray(); | |
return new String(text.Where(c => numericChars.Any(n => n == c)).ToArray()); | |
} | |
} |
The following code creates a new rule that returns green if the value is greater than 10 and red if it is not. This rule is serialized and stored in the Name ╰ TX Text Control .NET Server for ASP.NET
╰ TXTextControl Namespace
╰ TableCell Class
╰ Name Property
Gets or sets the cell's name. property of a table cell.
textControl1.Load("template.tx", TXTextControl.StreamType.InternalUnicodeFormat); | |
CellFilterInstructions cellFilterInstructions = new CellFilterInstructions() { | |
CompareValue = 10, | |
Operator = CellFilterInstructions.RelationalOperator.GreaterThan, | |
TrueColor = Color.Green, | |
FalseColor = Color.Red | |
}; | |
textControl1.Tables[1].Cells[2,2].Name = | |
JsonConvert.SerializeObject(cellFilterInstructions); | |
MailMerge mailMerge = new MailMerge(); | |
mailMerge.TextComponent = textControl1; | |
mailMerge.FieldMerged += MailMerge_FieldMerged; | |
string jsonData = File.ReadAllText("data.json"); | |
mailMerge.MergeJsonData(jsonData); |
In the FieldMerged event, this rule is deserted and evaluated. The color returned is then applied to the Table
╰ TXTextControl Namespace
╰ TableCellFormat Class
An instance of the TableCellFormat class represents the formatting attributes of a table cell. of the table cell.
private void MailMerge_FieldMerged(object sender, MailMerge.FieldMergedEventArgs e) | |
{ | |
// custom field handling | |
if (e.TableCell == null) | |
return; | |
if (e.TableCell.Name != "") | |
{ | |
CellFilterInstructions instructions = | |
(CellFilterInstructions)JsonConvert.DeserializeObject( | |
e.TableCell.Name, | |
typeof(CellFilterInstructions)); | |
// retrieve the color | |
Color? color = instructions.GetColor(e.MailMergeFieldAdapter.ApplicationField.Text); | |
// apply the color | |
if (color != null) | |
e.TableCell.CellFormat.BackColor = (Color)color; | |
} | |
} |
The following screenshot shows the result of this merge process:
Conclusion
The MailMerge class in TX Text Control provides a powerful and flexible solution for automating document generation processes. Using merge blocks and code-level manipulation, developers can easily create dynamic, data-driven documents. The FieldMerged event provides fine-grained control over the merge process, allowing developers to customize output based on specific conditions. This level of control is essential for complex document generation scenarios that require precise handling of data and content.