# Performance Best Practices

> All performance measures for WPF apps with TX Text Control and their effect, DefaultFormattingPrinter in App.xaml.cs and a background Stopwatch test of a merge.

- **Product:** TX Text Control .NET for WPF
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for WPF > Performance > Performance Best Practices
- **2439 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/best-practices/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/best-practices/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/best-practices/llms-full.txt

---

Before tuning a WPF application with TX Text Control, check the decisions that matter most: whether document work leaves the dispatcher free, whether every `ServerTextControl` is disposed, in which format documents live while they change, and whether the application waits for a printer when it starts. The table below lists these and the other performance measures with their effect, as Text Control has measured or described it, and the page with the code.

### At a Glance

| Practice | Effect | Read |
|---|---|---|
| Run long document work on other threads | The window stays responsive, and the user can cancel | [Background Operations](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/llms-full.txt) |
| Touch WPF elements only on the UI thread | No cross-thread exceptions; results reach the editor through `await` or the `Dispatcher` | [Dispatcher and UI Thread Access](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/dispatcher/llms-full.txt) |
| Use `ServerTextControl` for documents the user doesn't see | No undo history and no rendering for the screen | [Using ServerTextControl in Desktop Applications](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/servertextcontrol/llms-full.txt) |
| Dispose every `ServerTextControl` | `Create` keeps working; Windows allows 10,000 USER and 10,000 GDI handles per process | [Memory Usage and Resource Disposal](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/memory-usage/llms-full.txt) |
| Insert formatted text with Selection objects | About twice as fast as inserting, selecting and formatting, in a server benchmark | [Large Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/large-documents/#build-large-content-in-one-step) |
| Switch off `IsUndoEnabled` while code builds a document in the editor | Saves time and memory, according to the reference | [Undo and Redo](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-editor/editing-text/undo-redo/#switch-the-buffer-off) |
| Keep documents in the TX Text Control format until they're final | No conversion when loading and saving | [Document Loading and Conversion](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/loading-conversion/llms-full.txt) |
| Convert a batch with one `ServerTextControl` | 0.46 seconds for one large file, 0.18 seconds per file in a batch of 400 | [Large Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/large-documents/#replace-text-in-many-large-files) |
| Split long merge blocks | Two blocks merged faster than one block with the same rows | [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/#design-faster-templates) |
| Merge large batches in separate processes | 100 merges in 14 instead of 45 seconds on 16 cores | [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/#merge-large-batches-in-separate-processes) |
| Compress images in PDF files | 14.68 MB down to 1.48 MB in a test document | [Large Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/large-documents/#keep-pdf-output-small) |
| Set a default formatting printer | No wait for a slow printer when a control is created | [Start Without Waiting for a Printer](#start-without-waiting-for-a-printer) |
| Measure your own workload | Every benchmark depends on its documents and hardware | [Measure Your Own Workload](#measure-your-own-workload) |

### Start Without Waiting for a Printer

The layout of a document follows the device named by [FormattingPrinter](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/formattingprinter-property/llms-full.txt), so that the screen shows what the printer prints. That device is asked for information whenever a control is created, and a printer that answers slowly, such as a network printer in energy saving mode, delays the start of the window. Even with `FormattingPrinter` set to `"Display"`, TX Text Control contacts the default printer while it initializes.

Since version 30.0, the static property `DefaultFormattingPrinter` sets the device before the first instance exists; TX Text Control then doesn't access any other printer or the print spooler. In a WPF application, set it when the application starts, in *App.xaml.cs*:

```csharp
using System.Windows;

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        // Before the window with the TextControl is created
        TXTextControl.WPF.TextControl.DefaultFormattingPrinter = "Display";
        TXTextControl.ServerTextControl.DefaultFormattingPrinter = "Display";

        base.OnStartup(e);
    }
}
```

`"Display"` is a screen device that formats text the same way on every machine. Documents that must match the output of one particular printer exactly need that printer as formatting device instead. The blog article [Initialize Text Control Without a Printer (Completely)](https://www.textcontrol.com/blog/2022/02/28/initialize-text-control-without-a-printer-completely/) introduces the property with a Windows Forms example and describes it for `ServerTextControl` instances in WPF applications as well; the WPF `TextControl` has the same static property. `DefaultFormattingPrinter` has no page in the reference.

### Measure Your Own Workload

The numbers in the table were measured with Text Control's documents and hardware, mostly with TX Text Control .NET Server, and not every article names the hardware. Measure the steps of your own documents before you change code. The following handler times the creation, loading, merging and saving of the files of [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/#merge-in-the-background-with-progress-and-cancel), on a background thread as in your application:

```csharp
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using TXTextControl;
using TXTextControl.DocumentServer;

private async void Measure_Click(object sender, RoutedEventArgs e)
{
    string report = await Task.Run(() =>
    {
        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("invoice.pdf", StreamType.AdobePDF);
            long save = stopwatch.ElapsedMilliseconds;

            return $"Create: {create} ms\nLoad: {load} ms\nMerge: {merge} ms\nSave: {save} ms";
        }
    });

    MessageBox.Show(this, report, "Measurement");
}
```

The code works as follows:

- `Stopwatch` of `System.Diagnostics` measures each step; `Restart` sets it back to zero before the next one.
- The work runs in `Task.Run`, like the background code it stands for, and returns its report as a string, which the handler shows on the UI thread.
- The first run includes one-time work of the process. Text Control's benchmarks warm up or average several runs, so run the measurement several times and compare the later results.

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

### Next Steps

- [Dispatcher and UI Thread Access](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/dispatcher/llms-full.txt) — the threading model of the WPF `TextControl`.
- [Background Operations](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/llms-full.txt) — progress, cancellation and parallel work.
- [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/llms-full.txt) — what a merge costs and how to merge large batches.
- [Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/llms-full.txt) — the overview of this section.

---

## Nearby

- **Previous:** [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/llms-full.txt)
- **Next:** [Troubleshooting](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/troubleshooting/llms-full.txt)
