WPF
Background Worker
Windows Presentation Foundation
multithreading
C#

How to use WPF Background Worker

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

BackgroundWorker is an older but still understandable way to run long work off the WPF UI thread. Its main value is simple progress reporting and completion callbacks without freezing the interface, although in newer WPF code many teams prefer Task, async, and await for the same job.

Configure the Worker and Its Events

A BackgroundWorker runs work on a background thread through DoWork, reports progress through ProgressChanged, and returns to the UI thread with RunWorkerCompleted.

csharp
1using System.ComponentModel;
2using System.Threading;
3using System.Windows;
4
5public partial class MainWindow : Window
6{
7    private readonly BackgroundWorker _worker = new BackgroundWorker();
8
9    public MainWindow()
10    {
11        InitializeComponent();
12
13        _worker.WorkerReportsProgress = true;
14        _worker.WorkerSupportsCancellation = true;
15        _worker.DoWork += Worker_DoWork;
16        _worker.ProgressChanged += Worker_ProgressChanged;
17        _worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
18    }
19
20    private void StartButton_Click(object sender, RoutedEventArgs e)
21    {
22        if (!_worker.IsBusy)
23        {
24            _worker.RunWorkerAsync();
25        }
26    }
27}

This setup keeps the long-running work separate from button-click code and makes the lifecycle explicit.

Perform Work and Report Progress

The DoWork handler runs on a worker thread, so it is the place for long loops, CPU work, or blocking I/O that should not freeze the UI.

csharp
1private void Worker_DoWork(object? sender, DoWorkEventArgs e)
2{
3    var worker = (BackgroundWorker)sender!;
4
5    for (int i = 0; i <= 100; i++)
6    {
7        if (worker.CancellationPending)
8        {
9            e.Cancel = true;
10            return;
11        }
12
13        Thread.Sleep(30);
14        worker.ReportProgress(i);
15    }
16}

Use ReportProgress sparingly. Updating the UI too often can become its own performance problem.

Update the UI Safely

ProgressChanged and RunWorkerCompleted execute on the UI thread, so you can safely touch WPF controls there.

csharp
1private void Worker_ProgressChanged(object? sender, ProgressChangedEventArgs e)
2{
3    ProgressBar.Value = e.ProgressPercentage;
4}
5
6private void Worker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
7{
8    if (e.Cancelled)
9    {
10        StatusLabel.Content = "Operation cancelled";
11    }
12    else if (e.Error != null)
13    {
14        StatusLabel.Content = $"Error: {e.Error.Message}";
15    }
16    else
17    {
18        StatusLabel.Content = "Operation completed";
19    }
20}

This is one of the reasons BackgroundWorker was popular: it hides most of the explicit thread-marshaling ceremony.

Support Cancellation Intentionally

If you expose a cancel button, connect it to CancelAsync and make sure the worker actually checks CancellationPending.

csharp
1private void CancelButton_Click(object sender, RoutedEventArgs e)
2{
3    if (_worker.IsBusy)
4    {
5        _worker.CancelAsync();
6    }
7}

Cancellation is cooperative. If your DoWork handler never checks for it, the cancel request changes nothing.

When to Prefer Task and async

BackgroundWorker still works, but it is not the only option. In newer WPF code, Task.Run, async, and await often produce cleaner code, especially when your long-running work is already naturally asynchronous.

That said, BackgroundWorker remains understandable and serviceable in legacy WPF applications. If the surrounding codebase already uses it, consistency may matter more than modernization for its own sake.

Background workers can also accept startup arguments through RunWorkerAsync(argument), which is often cleaner than reading shared mutable state from inside DoWork. Passing explicit input makes the worker easier to test and reason about.

Common Pitfalls

Trying to update WPF controls directly inside DoWork breaks thread-affinity rules because that handler is not on the UI thread.

Calling CancelAsync without checking CancellationPending inside the worker creates fake cancellation support.

Reporting progress too frequently can make the UI busy even though the work is technically off the main thread.

Summary

  • 'BackgroundWorker runs long work off the WPF UI thread.'
  • Use DoWork for the background operation, ProgressChanged for UI progress, and RunWorkerCompleted for final UI updates.
  • Cancellation is cooperative and must be checked explicitly.
  • In modern WPF, Task and async are often cleaner, but BackgroundWorker is still valid in legacy-style applications.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.