Change Wpf Controls from a Non-Main Thread Using Dispatcher.Invoke

Change WPF controls from a non-main thread using Dispatcher.Invoke

The first thing is to understand that, the Dispatcher is not designed to run long blocking operation (such as retrieving data from a WebServer...). You can use the Dispatcher when you want to run an operation that will be executed on the UI thread (such as updating the value of a progress bar).

What you can do is to retrieve your data in a background worker and use the ReportProgress method to propagate changes in the UI thread.

If you really need to use the Dispatcher directly, it's pretty simple:

Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.progressBar.Value = 50));

WPF: dispatcher.invoke do not running

Since WPF controls have thread affinity it doesn't make much sense to create them on a background thread in most cases.

If you want to wait for 2 seconds before you show the login page, you could either use a DispatcherTimer or wait asynchronously:

public partial class WelcomePage : Page
{
public WelcomePage(MainWindow parent)
{
InitializeComponent();
this.parent = parent;

ShowLoginForm();
}

private MainWindow parent;

private async void ShowLoginForm()
{
await Task.Delay(2000);
this.parent.GoToLoginForm();
}
}

Then you won't need any calls to Dispatcher.Invoke.

Waiting for Dispatcher.Invoke in WPF UI thread

This sort of shutdown deadlock is exactly why you shouldn't use Invoke for this purpose.

Change it to BeginInvoke(), and for communications back to the worker thread use an event.

Delegate is never executed while invoked via Dispatcher.BeginInvoke with ContextIdle priority

I've finally solved it.
My issue was that the UI dispatcher queue kept constantly getting actions with a higher priority (higher than context-idle).

If you write a code as the following:

public MainWindow()
{
InitializeComponent();
Thread t = new Thread(() =>
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => DoBackGround1()), DispatcherPriority.Background, null);
Application.Current.Dispatcher.BeginInvoke(new Action(() => Debug.WriteLine("Context-Idle")), DispatcherPriority.ContextIdle, null);
Application.Current.Dispatcher.BeginInvoke(new Action(() => Debug.WriteLine("Additional-Background")), DispatcherPriority.Background, null);



});
t.Name = "dispatcherTest";
t.Start();
}


private void DoBackGround1()
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
Debug.WriteLine("Background " + i);
}
}

You will get the following result in the output window:

Background 0

Background 1

Background 2

Background 3

Background 4

Background 5

Background 6

Background 7

Background 8

Background 9

Additional-Background

Context-Idle

Additional-Background action is performed before the context-idle action, even though it was queued into the UI queue dispatcher after the context-idle action. It's trivial, but it is important to understand this concept.

I had a code that checks if I'm on the UI thread - If so, perform the action immediately. Otherwise, queue it into the dispatcher queue. When I forced the mentioned action (ExpandFreeDraw) into the UI queue (even if I was already on the UI thread), it all worked for me: now the action is invoked and I also get the needed validation.

Why do WPF tabs become unresponsive when `Dispatcher.Invoke()` is called?

Your complete code is executing on a single thread. You can't execute concurrent operations using a single thread. What you are currently doing is to block the main thread twice (too long) by invoking two potentially long-running operations synchronously:

  1. Synchronous Dispatcher invocation using Dispatcher.Invoke:
Application.Current.Dispatcher.Invoke(() => {}, DispatcherPriority.Background);

  1. Synchronous thread sleep:
Thread.Sleep(TimeSpan.FromSeconds(3));

While executing these synchronous operations, the main thread is not free to execute the logic (which is in this case part of the Selector, which is a superclass of TabControl) that manages the selection state of the hosted items.

The main thread is blocked by waiting for the Dispatcher to return and then by sending it to sleep i.e. suspending it.

As a result the Selector is not able to unselect the previously selected TabItem.

The Selector is able to cancel the selection procedure, which involves handling the selected item and unselecting every other item (in case multi-select is not supported). Obviously, the Selector cancels the unselection/processing of the pending items.

You should be able to test this by listening to the Selector.Unselected event which you attach to the TabItem. It should not be raised. Apparently the blocking of the main thread creates a race condition for the internal item validation of the Selector.

To fix this race condition it should be sufficient to increase the DispatcherPriority of the queued dispatcher messages to at least DispatcherPriority.DataBind (above DispatcherPriority.Input):

Application.Current.Dispatcher.Invoke(() => {}, DispatcherPriority.DataBind);

This is not the recommended fix, although it fixes the race condition and therefore the issue of multiple selected tabs as the critical code can now execute in time. The real underlying problem is the blocked main thread (which actually is a blocked Dispatcher).

You never want to block the main thread. Now you understand why.

For this reason .NET introduced the TPL. Additionally, the compiler/runtime environment allows true asynchronous execution: by delegating execution to the OS, .NET can use kernel level features like interrupts. This way .NET can allow the main thread to continue e.g., to process essential UI related events like device input.

Part of the interface between OS level and framework level is the Dispatcher and the InputManager. The Dispatcher basically manages the associated thread. In an STA application this is the main thread. Now, when you block the main thread using Thread.Sleep, the Dispatcher can't continue to work on the message queue that contains handlers that are executed on the associated dispatcher thread (main thread).

The Dispatcher is now unable to execute the input events posted by the InputManager. Since the dependency property system (on which routed events and the data binding engine is based on) and usually the code of UI controls are also executed on the Dispatcher, they are also suspended.

The combination of the very low dispatcher priority DispatcherPriority.Background in conjunction with the long Thread.Sleep makes the problem even worse.


The solution is to not block the main thread:

  1. Post work to the Dispatcher asynchronously and allow the application to continue while the job is enqueued and pending by calling Dispatcher.InvokeAsync:
Application.Current.Dispatcher.InvokeAsync(() => {}, DispatcherPriority.Background);

  1. Execute blocking I/O bound operations asynchronously using async/await:
await Task.Delay(TimeSpan.FromSeconds(3));

  1. Execute blocking CPU bound operations concurrently:
Task.Run(() => {});

Your fixed code would look as followed:

private async Task SimulateNoBugAsync(int id)
{
var delay = TimeSpan.FromSeconds(3);
UpdateStatus($"Bug simulation started... ID={id}, Delay={delay}");

// If you need to wait for a result or for completion in general,
// await the Dispatcher.InvokeAsync call.
Application.Current.Dispatcher.InvokeAsync(() => {}, DispatcherPriority.Background);
await Task.Delay(delay);

UpdateStatus($"Bug simulation complete. ID={id}");
}

Dispatcher.Invoke() with parameter always throws an InvalidOperationException

You can only access a control on the thread on which it was originally created so creating the control on a background thread and then try to use it or modify it back on the UI thread is not an option. That's why you get an InvalidOperationException.

Is it really not possible, or am I missing something?

It is indeed possible to create a control on a STA thread:

The calling thread must be STA, because many UI components require this

...but you still won't be able to use the control on another thread so this would be kind of pointless in your scenario I guess.

So no, you should create all your controls on the same thread. That is the thread on which the parent window was created, i.e. typically the main thread.

There are some very limited exceptions to this rule. Please refer to the following blog post for more information about this: https://blogs.msdn.microsoft.com/dwayneneed/2007/04/26/multithreaded-ui-hostvisual/. If your control doesn't require some kind of interactivity you could for example use a HostVisual as described in this post.

You could also launch an entire top-level window in its own thread: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/.

But except from this it doesn't make any sense to create more than one UI thread in a WPF application. You will simply have to make your controls render faster somehow instead of trying to offload the rendering work to another thread because this won't work.

When is a dispatcher needed in WPF

Almost any call to a WPF UI element should happen on the main thread of the application.
This is usually done through the methods of the Dispatcher associated with this thread.
The dispatcher can be obtained from any UI element, but usually it is obtained from the application: Applicatiion.Current.Dispatcher.

If you do not assign values ​​directly to the properties of UI elements, but use bindings for this, then the bindings mechanism has built-in marshaling of value assignment into the flow of the UI element.
Therefore, the INotifyPropertyChanged.PropertyChanged event can be raised on any thread.

When the observable collection changes, the INotifyCollectionChanged.CollectionChanged event is raised.
It is not provided for automatic marshaling to the UI thread.
But the collection can be synchronized with the BindingOperations.EnableCollection (...) method.
Then it can be changed (using synchronization) in any thread.
If such synchronization has not been performed, then it can be changed only in the UI thread.

In addition to these events, there is also ICommand.CanExecuteChanged.
There are no additional marshaling or synchronization methods provided for it.
Therefore, it can be raised only in the UI thread.
This is usually built into the WPF implementation of ICommand and the programmer doesn't need to worry about it.
But in simple (mostly educational) implementations, there is no such marshaling.
Therefore, when using them, the programmer himself must take care of the transition to the UI thread to raise this event.

So basically in MVVM practice you no matter what you would have to use a dispatcher to use BindingOperations.EnableCollectionSynchronization(fooCollection, _mylock)); correct?

Yes.
For the application of the collection, you understood correctly.

Here only the problem of separation of the View and ViewModel functionality arises.
You can only call "EnableCollectionSynchronization" on the View or on the main UI thread.
But you are implementing the collection in the ViewModel.
Also, in order not to clog up memory, when deleting a collection (replacing it with another, clearing the bindings that use it, replacing the ViewModel instance, etc.), you need to delete the created synchronization using the "DisableCollectionSynchronization (collection)" method.

In this regard, if a single instance of the ViewModel is used throughout the application session, then using "EnableCollectionSynchronization ()" is the simplest solution.

Example:

    public class MainViewModel
{
public ObservableCollection<int> Numbers { get; }
= new ObservableCollection<int>();

protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainViewModel()
{
if (Dispatcher.CheckAccess())
{
BindingOperations.EnableCollectionSynchronization(Numbers, ((ICollection)Numbers).SyncRoot);
}
else
{
Dispatcher.Invoke(()=>BindingOperations.EnableCollectionSynchronization(Numbers, ((ICollection)Numbers).SyncRoot));
}
}
}

But if many VM instances are used, with mutual nesting and dynamic addition and removal (for example, this can be the case when implementing a tree and viewing it in a TreeView), then using "EnableCollectionSynchronization ()" becomes not trivial.
If you do as I showed in the example above, then due to the fact that the reference to the synchronization object, to the collection, will be saved, they will not be deleted from memory and, accordingly, unnecessary instances of ViewModel will not be deleted.
And this will lead to a memory leak.
Therefore, in practice, marshaling of collection changes to the UI thread is often used.

It is also possible to embed in the INotifyCollectionChanged implementation, as well as marshaling to the UI thread, and calling "EnableCollectionSynchronization () / DisableCollectionSynchronization ()" while subscribing / unsubscribing to the CollectionChanged event.
The implementation of the ObservableCollection does not have this, but there are custom implementations of the observable collections in various packages where the similar is implemented.
And their use frees the programmer from creating routine, specific, repetitive code.

Unfortunately, I cannot tell you exactly what the package contains the required implementation.
I prefer to use my own implementations.

synchronize wpf dispatcher.invoke from multiple background threads

There is no need to synchronize these calls. Dispatcher.Invoke effectively acts as a queue for your calls already, and doesn't require synchronization.



Related Topics



Leave a reply



Submit