# UI Threading and Background Operations

> Run TX Text Control work off the Windows Forms UI thread: ServerTextControl in Task.Run, InvokeRequired, BeginInvoke, progress, cancel and parallel loops.

- **Product:** TX Text Control .NET for Windows Forms
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for Windows Forms > Performance > UI Threading and Background Operations
- **3426 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/threading/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/threading/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/threading/llms-full.txt

---

A Windows Forms application has one thread that draws the form and handles every click, and the `TextControl` lives on it. Document work that takes seconds, such as exporting a long document or converting a folder of files, freezes the form when it runs there. The rules below decide which TX Text Control class may run on which thread, and the samples move work to the background, report progress, cancel it and bring the result back to the editor.

### Which Thread Runs What

| Class | Thread | Why |
|---|---|---|
| `TextControl`, `ButtonBar`, `RulerBar`, `StatusBar`, the ribbon and the sidebar | The UI thread that created the form | They are Windows Forms controls, derived from `System.Windows.Forms.Control` |
| [ServerTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/servertextcontrol/llms-full.txt) | Any thread, one instance per thread | A component without a window, meant for work in the background |
| [MailMerge](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge/llms-full.txt) | The thread of the text component it is connected to | It works on the document of its `TextComponent` |

`TextControl` inherits the members that Windows Forms provides for threads from `System.Windows.Forms.Control`: `InvokeRequired` tells whether the caller is on another thread than the one the control was created on, and `Invoke` and `BeginInvoke` run a delegate on the control's thread. Everything else of the `TextControl`, from `Load` to `Selection`, is called on the UI thread only.

`ServerTextControl` was made for the other threads. Text Control lists support for several threads among its advantages over the visible control, together with better performance and a smaller memory footprint, because it keeps no undo history and renders nothing for the screen. Each thread creates, uses and disposes its own instance.

`Load`, `Save` and the merge methods of `MailMerge` are synchronous. They keep the calling thread busy until they return, so the thread you call them on decides whether the form responds in the meantime.

### Save the Editor's Document in the Background

The document of the editor can't go to another thread, but a copy of it can. The following handler saves the editor's document as a byte array on the UI thread, creates the PDF in a `ServerTextControl` on a thread of the thread pool and returns to the form when it's done. It belongs in *Form1.cs*, with a button named `exportPdfButton` on the form:

```csharp
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using TXTextControl;

private async void exportPdfButton_Click(object sender, EventArgs e)
{
    // The editor belongs to the UI thread: copy its document here
    textControl1.Save(out byte[] document, BinaryStreamType.InternalUnicodeFormat);

    exportPdfButton.Enabled = false;

    try
    {
        await Task.Run(() =>
        {
            using (ServerTextControl tx = new ServerTextControl())
            {
                if (!tx.Create())
                {
                    throw new InvalidOperationException("TX Text Control could not be initialized.");
                }

                tx.Load(document, BinaryStreamType.InternalUnicodeFormat);
                tx.Save("document.pdf", StreamType.AdobePDF);
            }
        });

        MessageBox.Show(this, "document.pdf created.");
    }
    catch (FilterException exception)
    {
        MessageBox.Show(this, $"{exception.Reason}: {exception.Message}", "Export failed");
    }
    finally
    {
        // After await, the handler runs on the UI thread again
        exportPdfButton.Enabled = true;
    }
}
```

The code works as follows:

- [TextControl.Save](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/save-method/llms-full.txt) with [BinaryStreamType](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/binarystreamtype/enumeration/llms-full.txt) `InternalUnicodeFormat` copies the complete document into a byte array. The user can go on editing while the copy is exported.
- The lambda passed to `Task.Run` uses only the byte array and its own `ServerTextControl`. [Create](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/servertextcontrol/create-method/llms-full.txt) returns `false` if the instance couldn't be initialized.
- `await` returns to the UI thread, so the message box and the button are used on the right thread. An exception from the task is thrown again there, and the `catch` block handles a [FilterException](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/filterexception/llms-full.txt) of the export.

[Handle Errors of Background Work](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/exceptions/#handle-errors-of-background-work) in Exceptions describes the error handling in more detail.

### Bring a Result Back to the Editor

A document created in the background goes into the editor on the UI thread. After `await`, the code already runs there. Code that isn't awaited, such as a callback that another component calls on its own thread, uses `InvokeRequired` and `BeginInvoke` of the `TextControl`:

```csharp
private void ShowDocument(byte[] document)
{
    // Called from another thread: repeat the call on the UI thread
    if (textControl1.InvokeRequired)
    {
        textControl1.BeginInvoke(new Action<byte[]>(ShowDocument), document);
        return;
    }

    textControl1.Load(document, BinaryStreamType.InternalUnicodeFormat);
}
```

`BeginInvoke` queues the call and returns at once, so the background thread doesn't wait for the editor. [TextControl.Load](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/load-method/llms-full.txt) then replaces the document of the editor on the UI thread.

### Convert Many Files With Progress and Cancel

Several documents can be processed on several threads at the same time, each with its own `ServerTextControl`. The following handlers convert all DOCX files of the folder *Documents* to PDF on up to two threads, move a `ProgressBar` named `progressBar1` and stop when the user clicks a button named `cancelButton`:

```csharp
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TXTextControl;

private CancellationTokenSource cancellation;

private async void convertFolderButton_Click(object sender, EventArgs e)
{
    string[] files = Directory.GetFiles("Documents", "*.docx");
    int converted = 0;

    progressBar1.Maximum = files.Length;
    progressBar1.Value = 0;
    convertFolderButton.Enabled = false;

    // Reports arrive on the UI thread, which created the Progress object
    IProgress<int> progress = new Progress<int>(count => progressBar1.Value = count);
    cancellation = new CancellationTokenSource();

    ParallelOptions options = new ParallelOptions
    {
        MaxDegreeOfParallelism = 2,
        CancellationToken = cancellation.Token
    };

    try
    {
        await Task.Run(() => Parallel.ForEach(files, options, file =>
        {
            // Each thread works with an instance of its own
            using (ServerTextControl tx = new ServerTextControl())
            {
                tx.Create();
                tx.Load(file, StreamType.WordprocessingML);
                tx.Save(Path.ChangeExtension(file, ".pdf"), StreamType.AdobePDF);
            }

            progress.Report(Interlocked.Increment(ref converted));
        }));
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show(this, $"Canceled after {converted} documents.");
    }
    finally
    {
        convertFolderButton.Enabled = true;
        cancellation.Dispose();
        cancellation = null;
    }
}

private void cancelButton_Click(object sender, EventArgs e)
{
    cancellation?.Cancel();
}
```

The code works as follows:

- `Parallel.ForEach` runs the loop body for several files at the same time, and `MaxDegreeOfParallelism` limits how many. The whole loop runs inside `Task.Run`, so the UI thread stays free while it waits.
- Every loop body creates, initializes and disposes its own `ServerTextControl`; no instance is shared between threads.
- `Interlocked.Increment` counts the finished files safely from several threads, and `Progress<int>` hands each count to the UI thread.
- `cancelButton_Click` runs on the UI thread while the conversion goes on. `Cancel` makes `Parallel.ForEach` start no further files and end with an `OperationCanceledException`; the files already started are finished first.

### How Far Threads Take You

More threads don't make document processing proportionally faster. TX Text Control is thread safe because it guards resources that all instances of a process share with critical sections, code that only one thread runs at a time. Threads that need such a resource at the same time wait for each other, which Text Control describes as safe but, for specific applications, slower.

- **Measure the number of threads.** Text Control publishes no fixed number for desktop applications. Start with a small `MaxDegreeOfParallelism`, raise it while the total time goes down and keep the value where it stops improving.
- **Use processes for large batches.** For merges of hundreds or thousands of documents, Text Control recommends several processes instead of several threads, as [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/mail-merge/#merge-large-batches-in-separate-processes) describes.
- **Set cultures explicitly.** [MailMerge.MergeCulture](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver/mailmerge/mergeculture-property/llms-full.txt) formats numbers and dates, and its default is the culture of the current thread. On a thread of the thread pool, that's not necessarily the culture your form uses, so set it in each merge that runs in the background.

### Next Steps

- [Using ServerTextControl in Desktop Applications](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/servertextcontrol/llms-full.txt) — the lifecycle of `ServerTextControl` and a first background task.
- [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/mail-merge/llms-full.txt) — merge in the background with progress, and in separate processes.
- [Memory Usage and Resource Disposal](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/memory-usage/llms-full.txt) — dispose every instance a thread creates.
- [Printing](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/printing/#print-without-blocking-the-form) — print on a background thread.

---

## Nearby

- **Previous:** [Performance](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/llms-full.txt)
- **Next:** [Large Documents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/performance/large-documents/llms-full.txt)
