TX Text Control .NET for WPF

Images and Barcodes

Topic

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 with X', 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 is false.

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:

A merged packing slip with a QR code in the upper right corner and a linear barcode at the end of each of its eight item rows.

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.Image or a MemoryStream make the compiler ask for the System.Drawing.Common package in a WPF project for .NET; the file overload doesn't. The full name TXTextControl.Image avoids a clash with the WPF Image control of System.Windows.Controls.
  • ImageCollection.Add with -1 inserts 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 Name of the frame, and its Text is 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:

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