# Background Operations

> Convert many files on background threads in WPF with ServerTextControl and Parallel.ForEach, with Progress, a cancel button and the limits of threads.

- **Product:** TX Text Control .NET for WPF
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for WPF > Performance > Background Operations
- **2924 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/background-operations/llms-full.txt

---

Converting a folder of contracts, merging a batch of letters or printing a long report can take minutes, and none of it needs the window. Run such work on threads of the thread pool with `ServerTextControl`, and the WPF window keeps responding: the user sees progress, can cancel and goes on editing. The patterns below build on the threading model of [Dispatcher and UI Thread Access](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/dispatcher/llms-full.txt) and show where threads stop helping.

### Choose Where the Work Runs

| Work | Where it runs | Page |
|---|---|---|
| A change the user makes or watches in the editor | The UI thread, in the WPF `TextControl` | [Large Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/large-documents/llms-full.txt) |
| One long task, such as a PDF export or a conversion | One background thread with one `ServerTextControl` | [Dispatcher and UI Thread Access](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/dispatcher/#hand-a-document-to-another-thread) |
| Many independent files | Several background threads, each with its own `ServerTextControl` | [Convert Many Files With Progress and Cancel](#convert-many-files-with-progress-and-cancel) |
| A merge with many records | A background thread with `ServerTextControl` and `MailMerge` | [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/#merge-in-the-background-with-progress-and-cancel) |
| Hundreds or thousands of merges | Several processes | [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/#merge-large-batches-in-separate-processes) |

A [ServerTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/llms-full.txt) is made for the background: it has no user interface, and Text Control names support for several threads, better performance and a smaller memory footprint as its advantages over a visible control. Every thread creates, initializes and disposes its own instance.

### Convert Many Files With Progress and Cancel

The following window converts all DOCX files of the folder *Documents* to PDF on up to two threads at a time, shows the progress and stops when the user clicks **Cancel**. The buttons and the progress bar sit above the editor in *MainWindow.xaml*:

```xml
<Window x:Class="SampleApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:WPF="clr-namespace:TXTextControl.WPF;assembly=TXTextControl.WPF"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel>
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <Button x:Name="convertFolderButton" Content="Convert Folder" Click="ConvertFolder_Click" />
            <Button x:Name="cancelButton" Content="Cancel" Click="Cancel_Click" />
            <ProgressBar x:Name="progressBar1" Width="200" Height="16" />
        </StackPanel>
        <WPF:TextControl x:Name="textControl1" />
    </DockPanel>
</Window>
```

The handlers go into *MainWindow.xaml.cs*:

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

private CancellationTokenSource cancellation;

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

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

    // Created on the UI thread, so its handler runs there
    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 =>
        {
            // One instance per file, never shared between threads
            using (ServerTextControl tx = new ServerTextControl())
            {
                tx.Create();
                tx.Load(file, StreamType.WordprocessingML);
                tx.Save(System.IO.Path.ChangeExtension(file, ".pdf"), StreamType.AdobePDF);
            }

            progress.Report(Interlocked.Increment(ref converted));
        }));
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show(this, $"Canceled after {converted} documents.");
    }
    catch (AggregateException exception)
    {
        // Parallel.ForEach collects the exceptions of its threads
        MessageBox.Show(this, exception.InnerExceptions[0].Message, "Conversion failed");
    }
    finally
    {
        convertFolderButton.IsEnabled = true;
        cancellation.Dispose();
        cancellation = null;
    }
}

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

The code works as follows:

- `Parallel.ForEach` converts several files at the same time, at most as many as `MaxDegreeOfParallelism` allows. It runs inside `Task.Run`, so the dispatcher stays free while the loop waits for its threads.
- Every iteration creates, initializes and disposes its own `ServerTextControl`. [Load](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/load-method/llms-full.txt) reads the DOCX file, and [Save](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/save-method/llms-full.txt) writes it with [StreamType](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/streamtype/enumeration/llms-full.txt) `AdobePDF`.
- `Interlocked.Increment` counts the finished files safely from several threads, and `Progress<int>` passes each count to the UI thread, where the `ProgressBar` may be changed.
- `Cancel_Click` runs on the UI thread while the conversion goes on, because the dispatcher is free. `Cancel` makes `Parallel.ForEach` start no further files and end with an `OperationCanceledException`; files that have started are finished first.
- An exception in any file, such as a [FilterException](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/filterexception/llms-full.txt) for a file that can't be loaded, ends the loop. `Parallel.ForEach` collects it in an `AggregateException`, which `await` throws again on the UI thread. To convert the other files anyway, catch `FilterException` inside the loop body instead.

### How Far Threads Take You

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

- **Measure the number of threads.** Text Control publishes no fixed value. Start with a small `MaxDegreeOfParallelism`, raise it as long as the total time goes down and keep the value where it stops improving.
- **Use processes for large batches.** For hundreds or thousands of merges, Text Control recommends several processes instead of several threads, and states that its approach applies to WPF applications as well. [Merge Large Batches in Separate Processes](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/#merge-large-batches-in-separate-processes) describes it.
- **Set cultures explicitly.** [MailMerge.MergeCulture](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/mergeculture-property/llms-full.txt) formats numbers and dates and defaults to the culture of the current thread. A thread of the thread pool may have another culture than the one your window uses, so set it in each merge that runs in the background.

### Print in the Background

Printing a long document is background work as well: a `ServerTextControl` loads the document and prints it on another thread. [Print a File in the Background](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/printing/#print-a-file-in-the-background) in Printing shows the code.

### Next Steps

- [Dispatcher and UI Thread Access](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/dispatcher/llms-full.txt) — which objects belong to which thread.
- [Mail Merge Performance](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/mail-merge/llms-full.txt) — merge in the background with progress, and in separate processes.
- [Exceptions and Error Handling](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/document-processing/exceptions/llms-full.txt) — catch errors of background work.
- [Memory Usage and Resource Disposal](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/memory-usage/llms-full.txt) — dispose every instance a thread creates.

---

## Nearby

- **Previous:** [Dispatcher and UI Thread Access](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/dispatcher/llms-full.txt)
- **Next:** [Large Documents](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/performance/large-documents/llms-full.txt)
