TX Text Control .NET for Windows Forms

Performance Best Practices

Topic

Most of what makes a Windows Forms application with TX Text Control feel fast comes down to a handful of decisions: which thread does the work, which class holds the document, in which format it is kept and how instances are created and disposed. The practices below collect those decisions in one place, each with the measurement behind it where Text Control has published one, and with the page that shows the code.

At a Glance

PracticeEffectRead
Keep document work off the UI threadThe form responds while documents are converted or mergedUI Threading and Background Operations
Use ServerTextControl for work the user doesn't seeNo undo history and no rendering for the screenUsing ServerTextControl in Desktop Applications
Dispose every ServerTextControlCreate keeps working; Windows allows 10,000 USER and 10,000 GDI handles per processMemory Usage and Resource Disposal
Build text with Selection objectsAbout twice as fast as inserting, selecting and formatting, in a server benchmarkLarge Documents
Switch the undo buffer off while code builds a document in the editorSaves time and memory, according to the referenceSwitch the Buffer Off
Keep documents in the TX Text Control format until they're finalNo conversion when loading and savingDocument Loading and Conversion
Use one ServerTextControl for a batch0.46 seconds for one large file, 0.18 seconds per file in a batch of 400Document Loading and Conversion
Split long merge blocksTwo blocks merged faster than one block with the same rowsMail Merge Performance
Merge large batches in separate processes100 merges in 14 instead of 45 seconds on 16 coresMail Merge Performance
Compress images in PDF files14.68 MB down to 1.48 MB in a test documentLarge Documents
Set a default formatting printerNo wait for a slow printer when a control is createdStart Without Waiting for a Printer
Measure your own workloadEvery benchmark depends on its documents and hardwareMeasure Your Own Workload

Start Without Waiting for a Printer

TX Text Control formats text with the dimensions of a printer, so that the document on the screen matches the printed one. The printer named by FormattingPrinter is contacted when a control is created, and a printer that responds slowly, such as a network printer in energy saving mode, delays the start. Even with FormattingPrinter set to "Display", the default printer is contacted during initialization.

Since version 30.0, the static property DefaultFormattingPrinter sets the formatting device before any instance is created, and TX Text Control then doesn't contact any other printer or the print spooler. Set it at the start of Program.cs:

using System;
using System.Windows.Forms;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();

        // Before the first TextControl or ServerTextControl is created
        TXTextControl.TextControl.DefaultFormattingPrinter = "Display";
        TXTextControl.ServerTextControl.DefaultFormattingPrinter = "Display";

        Application.Run(new Form1());
    }
}

"Display" formats with the font dimensions of the screen, which look the same on every machine, instead of those of a particular printer. Keep a printer as formatting device if your documents must match that printer's output exactly. The blog article Initialize Text Control Without a Printer (Completely) describes the property.

Measure Your Own Workload

Text Control publishes no fixed numbers for desktop applications, and each benchmark above ran with its own documents and hardware, often the server product. Your templates, data, images and fonts decide how long your documents take. The following handler measures the four steps of a merge separately, creating the instance, loading the template, merging and saving, so that you know which one to improve. It uses the files of Mail Merge Performance, invoice.docx and invoice.json:

using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using TXTextControl;
using TXTextControl.DocumentServer;

private void measureButton_Click(object sender, EventArgs e)
{
    string json = File.ReadAllText("invoice.json");
    Stopwatch stopwatch = Stopwatch.StartNew();

    using (ServerTextControl tx = new ServerTextControl())
    {
        tx.Create();
        long create = stopwatch.ElapsedMilliseconds;

        stopwatch.Restart();
        tx.Load("invoice.docx", StreamType.WordprocessingML, new LoadSettings
        {
            ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
            LoadSubTextParts = true
        });
        long load = stopwatch.ElapsedMilliseconds;

        stopwatch.Restart();
        using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
        {
            mailMerge.MergeJsonData(json);
        }
        long merge = stopwatch.ElapsedMilliseconds;

        stopwatch.Restart();
        tx.Save("invoices.pdf", StreamType.AdobePDF);
        long save = stopwatch.ElapsedMilliseconds;

        MessageBox.Show(this, $"Create: {create} ms\nLoad: {load} ms\nMerge: {merge} ms\nSave: {save} ms");
    }
}

The code works as follows:

  • Stopwatch from System.Diagnostics measures the elapsed time, and Restart starts each step at zero.
  • The first run of a process includes one-time work. Text Control's benchmarks warm up before they measure or average several runs, so click the button several times and compare the later results.
  • The measurement runs on the UI thread to keep the code short. Measure the same way inside Task.Run when you compare it with your background code.

Measure on computers like those of your users, with release builds and the documents they create.

Next Steps