How to update UI from another thread in C#
By FoxLearn 1/22/2025 7:33:53 AM 401
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 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
.
- How to Create a custom Progress Bar with Percentage in C#
- How to Create a Wait Form Dialog in C#
- How to Get all Forms and Open Form with Form Name in C#
- How to use Advanced Filter DataGridView in C#
- How to Print DataGridView with Header & Footer with Landscape in C#
- How to Add Combobox to DataGridView in C#
- How to Hide a WinForm in C#
- How to zoom an image in C#