Images and Barcodes
Merged documents often need more than text from the data: a customer's logo, a product photo or a QR code that encodes the invoice number. In a template, each of these is a placeholder, an image or a barcode with a name. MailMerge fills every placeholder whose name matches a field of the data and leaves its size and position as designed. The sections below create placeholders in the WPF editor and in code, merge images from Base64 data, files and SVG, merge values into barcodes and check those values beforehand.
The invoice of Merge Data into Fields gets a logo and a QR code for its invoice number. For images and barcodes added without a merge, see Images and Shapes and Charts and Barcodes.
How Placeholders Work
An image or barcode becomes a placeholder through its name, which matches a field of the data:
- Image data can be a byte array, a
System.Drawing.Image, a Base64 string, a hexadecimal string that starts withX', the name of an image file or, since version 32.0 SP3, SVG markup as plain XML text. - Size and layout stay. A merged image is scaled to the width of the placeholder and keeps its position and text flow.
- Barcodes keep their type, size and position, and only the encoded text changes.
- Inside a merge block, placeholders are merged once per record, for example a photo in each product row.
- Names follow the rules of merge field names, dot notation for related data included.
- Image placeholders without data stay in the document unless RemoveEmptyImages is
true; the default isfalse.
Save templates with placeholders in the TX Text Control format, here as invoice.tx, as Text Control's samples do.
Both kinds of placeholders can fill one document. The following packing slip was merged from JSON data: the QR code at the top and the linear barcode in each row of the item block received their values from the data. The blog article Enhancing Documents with QR Codes and Barcodes in .NET C#: A Comprehensive Guide shows its template and data:

Create Placeholders With the Ribbon
The Insert tab of the WPF ribbon inserts images and barcodes. To make one of them a placeholder, select it and type its name into the object name box of the Frame Formatting tab, the RibbonFrameLayoutTab, which edits images, text frames, drawings and barcodes. Connect that tab to the TextControl in XAML as described in Ribbon.
With a data source loaded into the Reporting tab, the Merge Fields group offers Select Frame Data Column while an image or barcode is selected, so that users pick the name from the columns of the data instead of typing it.
Create Placeholders in Code
A placeholder is a frame with a Name. The following handlers insert a logo placeholder and a QR code at the input position of textControl1. Add using TXTextControl.Barcode;, using TXTextControl.DataVisualization; and using TXTextControl.WPF.Barcode; to MainWindow.xaml.cs:
private void insertLogoPlaceholderButton_Click(object sender, RoutedEventArgs e)
{
// 4 is the filter index of PNG files
TXTextControl.Image placeholder = new TXTextControl.Image("placeholder.png", 4);
// Insert the image inline at the input position
textControl1.Images.Add(placeholder, -1);
// The name connects the placeholder with the data
placeholder.Name = "Logo";
}
private void insertQrCodeButton_Click(object sender, RoutedEventArgs e)
{
// The WPF barcode control of TXTextControl.WPF.Barcode
TXBarcodeControl qrCode = new TXBarcodeControl
{
BarcodeType = BarcodeType.QRCode,
// The size of the code is calculated for 12 characters
UpperTextLength = 12,
Text = "INV-0000-000"
};
BarcodeFrame barcodeFrame = new BarcodeFrame(qrCode)
{
// The name connects the barcode with the data
Name = "InvoiceNumber",
Size = new System.Drawing.Size(1440, 1440) // 1 inch in twips
};
textControl1.Barcodes.Add(barcodeFrame, -1);
}
The code works as follows:
- The Image constructor with a file name and filter index loads the placeholder picture. The overloads that take a
System.Drawing.Imageor aMemoryStreammake the compiler ask for the System.Drawing.Common package in a WPF project for .NET; the file overload doesn't. The full nameTXTextControl.Imageavoids a clash with the WPFImagecontrol ofSystem.Windows.Controls. - ImageCollection.Add with
-1inserts the image at the input position. Its size is the width of every merged image. - The QR code is inserted like any barcode, a WPF TXBarcodeControl in a BarcodeFrame, as Insert a Barcode From Code explains; the Windows Forms class of the same name can't be used here. What makes it a placeholder is the
Nameof the frame, and itsTextis shown only until the merge.
Because a barcode must not push the surrounding text away, its frame keeps its size and the code inside scales with the text: UpperTextLength is the length at which the code fills the frame. Set it to the longest value your data holds, before Text, as the blog article Understanding the UpperTextLength of Barcodes in TX Text Control X9 explains.
Add the Image Data
In JSON, an image travels as a Base64 string. Add a Logo property with the Base64 data of a PNG image to invoice.json:
[
{
"Company": "Contoso Ltd.",
"Name": "Jane Miller",
"InvoiceNumber": "INV-1001",
"Logo": "iVBORw0KGgoAAAANSUhEUgAA...",
"Items": [
{ "Description": "Consulting", "Quantity": 2, "Price": 150.00 },
{ "Description": "Training", "Quantity": 1, "Price": 480.00 }
]
}
]
Replace the shortened value with the complete Base64 string of your image; InvoiceNumber fills the QR code. A file name as value, such as "Logo": "logo.png", makes MailMerge load the file from the folder of SearchPath, or from the root directory when the search path is empty. SVG markup is passed as it is, without Base64 encoding; the blog article Service Pack 3: MailMerge Supports SVG Images has an example.
Merge Images and Barcodes
This handler merges invoice.tx, removes empty image placeholders and loads the result into the editor. It needs using System;, using System.IO; and using TXTextControl.DocumentServer;:
private void mergeImagesButton_Click(object sender, RoutedEventArgs e)
{
byte[] result;
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
tx.Load("invoice.tx", StreamType.InternalUnicodeFormat);
using (MailMerge mailMerge = new MailMerge
{
TextComponent = tx,
// Remove placeholders the data has no image for
RemoveEmptyImages = true,
// Image file names in the data are searched here
SearchPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images")
})
{
mailMerge.ImageMerged += (s, args) =>
{
if (args.Merged && args.Name == "Logo")
{
args.DescriptiveText = "Company logo";
}
};
mailMerge.BarcodeMerged += (s, args) =>
System.Diagnostics.Debug.WriteLine("Barcode merged: " + args.BarcodeFrame.Name);
mailMerge.MergeJsonData(File.ReadAllText("invoice.json"));
}
tx.Save(out result, BinaryStreamType.InternalUnicodeFormat);
}
textControl1.Load(result, BinaryStreamType.InternalUnicodeFormat);
}
The code works as follows:
- ImageMerged occurs for each image placeholder, with the Name of the placeholder, whether it was Merged and, in a block, the MergeBlockName.
- DescriptiveText replaces the descriptive text of the merged image. The Image property is a
System.Drawing.Image, which a WPF project for .NET uses through the System.Drawing.Common package. - BarcodeMerged follows each merged barcode, and BarcodeMergedEventArgs holds its BarcodeFrame.
The editor then shows the invoice with the logo in the size of the placeholder and a QR code for INV-1001.
Check Barcode Values
A value from the data may contain characters or have a length that a barcode type can't encode. The static methods of the WPF TXBarcodeControl check it before the merge:
string invoiceNumber = "INV-1001";
if (!TXBarcodeControl.IsTextValid(BarcodeType.QRCode, invoiceNumber, out string errorMessage))
{
MessageBox.Show(this, "Invalid value: " + errorMessage);
}
IsTextValid returns false and an error message for an invalid value. GetMinimumTextLength and GetMaximumTextLength return the allowed lengths of a type. Insert a Barcode From Code lists the 20 values of BarcodeType.
Image Merge Fields
Older templates merge images into merge fields named with the prefix image:, such as image:Logo. MailMerge still processes them, but their event, ImageFieldMerged, is marked obsolete in the assembly, and using it causes a compiler warning. New templates use named placeholders and ImageMerged.
Next Steps
- Merge Blocks: repeat images and barcodes for each record.
- Data Sources: the data that carries images and values.
- Charts and Barcodes: barcodes in the WPF editor.
- The blog article Merging Barcodes with JSON Data in C#: a QR code filled from JSON data.