How to update UI from another thread in C#
By FoxLearn 3/10/2025 9:16:40 AM 1.69K
If you try to modify UI controls from another thread, you'll encounter exceptions, such as InvalidOperationException in Windows Forms.
System.InvalidOperationException: 'Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.'
In this article, we will explore how to execute multiple threads concurrently, update the UI based on their results, and avoid the "cross-thread operation" error by using BeginInvoke
(or Invoke
), which ensures that UI updates are performed on the UI thread.
Update the UI from Another Thread in C# Windows Forms Application
In Windows Forms (WinForms) applications, UI controls, such as TextBox
, Label
, or DataGridView
, are created on the UI thread and can only be updated from that same thread. If you try to update the UI from a different thread, you'll encounter the exception shown above.
This can be done using the Control.BeginInvoke
or Control.Invoke
methods.
Invoke
: This method is synchronous, meaning the calling thread will wait for the UI thread to finish updating before continuing.BeginInvoke
: This method is asynchronous, meaning the calling thread can continue executing without waiting for the UI update.
We simulate running several tasks concurrently. Each task represents a simulated operation (e.g., a delayed HTTP GET request or a computational task). The results of these tasks will be logged in a TextBox
as each task completes.
The form contains a TextBox
(txtLog
) to display log messages, a Button
(btnStartThreads
) to start the tasks, and a NumericUpDown
(numThreads
) to allow the user to specify how many threads to run.
C# Update UI from Thread
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; // c# ui thread namespace ThreadingExample { public partial class frmUpdateUIThread : Form { Control control; public frmUpdateUIThread() { InitializeComponent(); control = txtLog; // This can be any control, like a TextBox } // Method to log messages to the UI thread private void Log(string msg) { string m = $"{DateTime.Now.ToString("H:mm:ss.fffff")}\t{msg}\n"; control.BeginInvoke((MethodInvoker)delegate () { txtLog.AppendText(m); // Append log message to the TextBox txtLog.ScrollToCaret(); // Scroll to the latest log entry }); } // Start multiple threads and log their results in the UI private async void btnStartThreads_Click(object sender, EventArgs e) { Random random = new Random(); List<Task> allTasks = new List<Task>(); for (int i = 1; i <= (int)numThreads.Value; i++) { var j = i; var delay = TimeSpan.FromMilliseconds(random.Next(1000, 5000)); // Simulate random work time var task = Task.Run(async () => { var tId = $"Task ID {j}, ThreadID = {Thread.CurrentThread.ManagedThreadId}"; Log($"{tId} starting processing"); await Task.Delay(delay); // Simulate some background work Log($"{tId} finished. Took {delay.TotalSeconds} seconds"); }); allTasks.Add(task); } // Wait for all tasks to finish await Task.WhenAll(allTasks); // This ensures that the main thread waits for all background tasks to complete before logging the final message. // Final log entry after all tasks are complete Log("All tasks have finished"); } } }
In this example:
- In the constructor, we assign the
TextBox
control (txtLog
) to thecontrol
variable. TheLog
method takes a message, appends a timestamp, and usesBeginInvoke
to safely update theTextBox
on the UI thread. - In the
btnStartThreads_Click
method, we create several background tasks (Task.Run
). Each task runs a simple loop with a random delay to simulate background work (e.g., a network request or computational task). The task logs a message to the UI thread when it starts and when it finishes. BeginInvoke
ensures that each log message is added to theTextBox
on the UI thread. This prevents the cross-thread operation error and ensures that the UI remains responsive.
Always remember that UI controls can only be updated on the UI thread. If you need to update the UI based on results from background threads, always use Invoke
or BeginInvoke
.
While BeginInvoke
is asynchronous, the method is still relatively efficient. However, you should avoid excessive UI updates (e.g., updating every millisecond) because frequent UI updates can cause the application to become unresponsive.
C# Update UI from Background Thread
You can use the Invoke
method to marshal the update to the UI thread for Windows Forms
For example, winforms update ui from background thread
// Background worker or any other background thread // ui thread update from private void BackgroundThreadMethod() { // Do some background work... // Once the work is done, update the UI on the main thread if (this.InvokeRequired) { this.Invoke(new Action(() => { // Update UI elements, e.g., a label or a button label1.Text = "Updated from background thread"; })); } else { // If already on the UI thread, update directly label1.Text = "Updated from background thread"; } }
In this example:
InvokeRequired
: This checks if the current thread is the UI thread. If not, it usesInvoke
to call the delegate on the UI thread.Invoke
: This is used to execute a method on the UI thread.
In WPF, you can use the Dispatcher
to update the UI from a background thread.
For example, wpf update ui from background thread
// Background worker or any other background thread private void BackgroundThreadMethod() { // Do some background work... // Once the work is done, update the UI on the UI thread Dispatcher.Invoke(() => { // Update UI elements, e.g., a label or a button label1.Content = "Updated from background thread"; }); }
You can also use the BackgroundWorker
class to handle background tasks and update the UI when the task is complete.
For example, c# update ui from background thread in winforms and wpf
BackgroundWorker backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += (sender, e) => { // Perform background task here System.Threading.Thread.Sleep(2000); // Simulate work }; backgroundWorker.RunWorkerCompleted += (sender, e) => { // Update UI when background work is done label1.Text = "Background work complete!"; }; // Start the background work backgroundWorker.RunWorkerAsync();
In applications where you need to run multiple tasks concurrently and update the UI based on their results, you must marshal the UI updates back to the UI thread. In WinForms, this can be done easily using Control.BeginInvoke
or Control.Invoke
.
WPF Update UI from another thread in C#
In a WPF (Windows Presentation Foundation) application, UI elements can only be updated from the main thread (also called the UI thread). If you need to update the UI from another thread, you need to use thread synchronization techniques, such as Dispatcher.Invoke()
or Dispatcher.BeginInvoke()
.
Assume we have a simple WPF application with a TextBlock
named myTextBlock
that we want to update from a background thread.
UI (MainThread) - XAML:
<Window x:Class="WpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Main Window" Height="350" Width="525"> <Grid> <TextBlock Name="myTextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20"/> </Grid> </Window>
C# Code behind
using System; using System.Threading; using System.Windows; namespace WpfApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // Start a background thread to update the UI Thread backgroundThread = new Thread(UpdateText); backgroundThread.Start(); } private void UpdateText() { // Simulate work in the background thread Thread.Sleep(2000); // Sleep for 2 seconds to mimic work // Now, update the UI from the background thread // Use Dispatcher to update UI safely from another thread Dispatcher.Invoke(() => { myTextBlock.Text = "Hello from the background thread!"; }); } } }
In this example:
Dispatcher.Invoke():
- The
Dispatcher.Invoke()
method is used to execute a delegate on the UI thread. In this example, we are using it to update theTextBlock
'sText
property from the background thread. Invoke()
is a synchronous operation, meaning it will block the calling thread until the action is completed on the UI thread.
- The
Threading:
- We create a
Thread
(backgroundThread
) and start it. This simulates a background operation that updates the UI after a 2-second delay (usingThread.Sleep()
).
- We create a
UI Thread Access:
- The UI controls (
myTextBlock
in this case) can only be modified from the UI thread, so theDispatcher.Invoke()
method allows us to marshal the update back to the UI thread.
- The UI controls (
You can also use Dispatcher.BeginInvoke()
instead of Invoke()
.
Dispatcher.BeginInvoke(new Action(() => { myTextBlock.Text = "Hello from the background thread!"; }));
BeginInvoke()
doesn't block the calling thread, making it a non-blocking operation, and it will execute the delegate asynchronously on the UI thread.
- How to Open and Show a PDF file in C#
- How to Get all Forms and Open Form with Form Name in C#
- How to zoom an image in C#
- How to Print a Picture Box in C#
- How to Search DataGridView by using TextBox in C#
- How to read and write to text file in C#
- How to save files using SaveFileDialog in C#
- How to Print DataGridView with Header & Footer with Landscape in C#