Change WPF controls from a non-main thread using Dispatcher.Invoke
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In WPF (Windows Presentation Foundation), all UI controls are owned by the main (UI) thread and can only be accessed from that thread. Attempting to modify a control from a background thread throws an InvalidOperationException: "The calling thread cannot access this object because a different thread owns it." The Dispatcher.Invoke and Dispatcher.BeginInvoke methods marshal calls from background threads back to the UI thread, allowing safe updates to the interface.
The Problem
Solution 1: Dispatcher.Invoke (Synchronous)
Dispatcher.Invoke marshals the call to the UI thread and blocks until it completes:
Or using the control's own dispatcher:
Solution 2: Dispatcher.BeginInvoke (Asynchronous)
BeginInvoke queues the action on the UI thread and returns immediately without blocking:
Invoke vs BeginInvoke
| Method | Blocking | Returns |
Invoke | Yes — waits for UI thread | After completion |
BeginInvoke | No — queues and returns immediately | DispatcherOperation |
Use Invoke when the background thread needs the result of the UI operation. Use BeginInvoke for fire-and-forget updates like progress bars and status labels.
Solution 3: async/await (Recommended for Modern C#)
The cleanest approach — async/await automatically marshals back to the UI thread:
With async/await, the code after await runs on the UI thread automatically — no Dispatcher.Invoke needed.
Solution 4: IProgress<T> for Progress Reporting
IProgress<T> is the standard pattern for reporting progress from background tasks:
Solution 5: Data Binding with INotifyPropertyChanged
Bind UI controls to a ViewModel. WPF marshals property change notifications to the UI thread automatically:
WPF's data binding engine automatically dispatches property change notifications to the UI thread, so you do not need explicit Dispatcher.Invoke calls.
Dispatcher Priority
Control when the queued action executes relative to other UI work:
| Priority | When it runs |
Send | Immediately (highest) |
Normal | After input processing |
Background | After all normal-priority items |
ApplicationIdle | When the application is idle |
Checking Thread Access
Common Pitfalls
- Deadlock with Invoke:
Dispatcher.Invokeblocks the calling thread. If the UI thread is also waiting for the background thread (e.g., withTask.Wait()), you get a deadlock. UseBeginInvokeorasync/awaitto avoid this. - Too many Invoke calls: Calling
Dispatcher.InvokeorBeginInvokein a tight loop (e.g., updating UI every millisecond) floods the dispatcher queue and freezes the UI. Batch updates or throttle with a timer. - Accessing Dispatcher on non-UI thread:
this.Dispatchermay not be initialized if called before the window is loaded. UseApplication.Current.Dispatcheras a safe fallback. - Closure variable capture in loops: When using
BeginInvokeinside a loop, the lambda captures the loop variable by reference. By the time it executes, the variable may have changed. Capture a local copy:int localI = i;. - Forgetting that async void is fire-and-forget:
async voidmethods (required for event handlers) swallow exceptions. Wrap the body in try/catch to handle errors.
Summary
- Use
Dispatcher.Invoke()for synchronous UI updates from background threads (blocks until complete) - Use
Dispatcher.BeginInvoke()for asynchronous fire-and-forget UI updates - Prefer
async/awaitwithTask.Run()for modern code — it automatically marshals back to the UI thread - Use
IProgress<T>for structured progress reporting from background tasks - Use MVVM data binding with
INotifyPropertyChanged— WPF handles thread marshaling for bound properties automatically

