Why is Dispatcher.Invoke not triggering UI update? - c#

I am trying to reuse a UserControl and also borrow some logic that keeps track of progress. I'll try and simplify things. MyWindow.xaml includes a MyUserControl. MyUserControl has its own progress indicator (Formatting in progress..., Copying files..., etc.) and I'd like to mirror this progress somewhere in the MyWindow form. But, the user control has some logic I don't quite understand. I've read and read but I still don't understand the Dispatcher. Here's a summary of the logic in the user control that updates the progress.
this.Dispatcher.Invoke(DispatcherPriority.Input, (Action)(() =>
{
DAProgressIndicator = InfiniteProgress.AddNewInstanceToControl(StatusGrid, new SolidColorBrush(new Color() { A = 170, R = 128, G = 128, B = 128 }), string.Empty);
DAProgressIndicator.Message = MediaCardAdminRes.ActivatingCard;
ActivateInProgress = true;
}));
I thought I'd be smart and add an event to MyUserControl that would be called in the ActivateInProgress property set logic.
public bool ActivateInProgress
{
get
{
return _activateInProgress;
}
set
{
_activateInProgress = value;
if (ActivateInProgressHandler != null)
{
ActivateInProgressHandler(value);
}
}
}
I'm setting the ActivateInProgressHandler within the MyWindow constructor to the following method that sets the view model property that is used for the window's own progress indicator.
private void SetActivation(bool activateInProgress)
{
viewModel.ActivationInProgress = activateInProgress;
}
However, the window's progress indicator never changes. So, I'm convinced that the Dispatcher.Invoke is doing something that I don't understand. If I put a message box inside the SetActivation method, the thread blocks and the window's progress indicator is updated. I understand basic threads but this whole Dispatcher thing is new to me. What am I missing?
UPDATE: It seems to be working now. It turns out the progress was being updated so fast that it never got shown on the screen. But, I still would like to understand why the Dispatcher.Invoke was done (this was existing code that I didn't write). Why aren't the action contents done in line with the rest of the *.xaml.cs code?

Your last paragraph mentions threads twice, which raises the possibility that there are one or more background threads. But since you didn't mention what threads exist in the application, how they are created, how they interact, etc, I'll assume for the moment there is only one thread.
If the UI thread is the only thread, the problem is obvious: Your UI thread is busy running the task in progress, and doesn't take time to render the updated UI. If that's the problem, this will probably fix it:
viewModel.ActivationInProgress = activateInProgress;
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new Action(() => {}));
The BeginInvoke forces all Dispatcher operations above input priority to complete before the current thread continues.

The Dispatcher works on a queue. So it could be that the UI thread is blocking. You add more work in the queue via the Dispatcher but it will never get executed because the UI thread is blocking.
Maybe try this:
DispatcherFrame _frame = new DispatcherFrame();
Dispatcher.PushFrame(_frame);
This will put your work infront of the work already on the queue. So the UI thread will do the work and then block again.

Related

Updating progressbar in parallel loop

I have a WinForm application and I am trying to update a progressbar in a parallel loop. Here is the snippet of my code:
Parallel.ForEach(files, (file, state) =>
{
//Intialization of parameters
//do cpu-intensive task
DoWork();
UpdateProgress();
});
int counter = 0;
private object updateLock = new object();
void UpdateProgress()
{
lock (updateLock)
{
counter++;
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(() => { progressBar1.SetProgress(counter); });
}
else
{
progressBar1.SetProgress(counter);
}
}
}
To get an instant update on progressbar animation I use the SetProgress.
public static void SetProgress(this ProgressBar bar, int value)
{
if (value == bar.Maximum)
{
bar.Maximum = value + 1;
bar.Value = value + 1;
bar.Maximum = value;
}
else
{
bar.Value = value + 1;
}
bar.Value = value;
}
The whole process seems to work fine, but I have a problem with the way progress bar is updated. Randomly I see the progress animations is set back and forth, say e.g go to 33/150, then to 31/150 and then to 32/150. Although I used a synchonization lock object to update progress on each step accordingly, it seems the messages in Main UI Thread are not processed in order or there is something wrong with the code.
Any ideas what might be the issue?
Thanks in advance.
[UPDATE]
The problem is related with how Parallel.ForEach works. You may think that it uses only background threads to do the work, but it actually uses the current thread too. In other words during the execution of the Parallel.ForEach, the current thread plays the role of a worker thread. In your case the current thread is the UI thread. The condition if (progressBar1.InvokeRequired) evaluates to true for the background threads involved in the operation, and false for the UI thread.
The background threads are calling the progressBar1.Invoke method in your example. Unlike the BeginInvoke, the Invoke is a blocking method, and will return only after the UI thread has processed the supplied delegate. Since the UI thread is busy processing its own partition of the files collection, the Invoke will block, so all background threads will get stuck, and the only thread that will continue making progress will be the UI thread. At the end the UI thread will have to wait for the other threads to deliver the result of the single file they received initially for processing, which they won't be able to do, so the Parallel.ForEach will deadlock. At least this is the expected outcome of the code you posted. Since you are not observing a deadlock, my guess is that there is some line of code missing in your example (a call to Application.DoEvents maybe?) that resolves the deadlock situation.
The easiest way to fix this unpleasant situation is by preventing the UI from becoming a worker thread. Just use the Task.Run method, to offload the whole parallel processing to a ThreadPool thread:
await Task.Run(() =>
{
Parallel.ForEach(//...
});
You will also have to mark your event handler with the async keyword, otherwise the compiler will not permit the usage of the nifty await operator.
After applying this fix, you may want to make your code more elegant by removing all this ugly InvokeRequired/Invoke stuff, and replacing it with a modern Progress object. This would also make trivially easy to seperate the files-processing logic from the UI-related logic, if you find it desirable from an architectural perspective. You can read this article if you want to learn how to use the Progress class.

How to remove SynchronizationContext until my BackgroundWorker has finished?

I'm trying to download some movie data in a BackgroundWorker thread, but when the background thread tries to modify ObservableCollection fields for the selected movie there is an error stating "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread".
First I tried removing any UI elements such as TextBoxes before running the BackgroundWorker, which strangely didn't seem to work even though there were no objects left to synchronize changes.
I can get around this by using the answer here to send the changes to the UI thread but it means I'd have to flood my background thread with many lines like uiContext.Send(x => _matchObsCollection.Add(match), null); which would make my code a little messier than I'd like.
Ideally I'd like to remove the SynchronizationContext so that the UI thread would not try to interfere with the background thread, but again this isn't making any difference. Is there something wrong with what I'm trying to do here:
// Prevent synchronization with the UI thread.
var uiContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
// Prepare the background worker for data download tasks.
BackgroundThread = new BackgroundWorker();
BackgroundThread.WorkerReportsProgress = true;
BackgroundThread.ProgressChanged += BackgroundThread_ProgressChanged;
BackgroundThread.DoWork += (f, arg) =>
{
DownloadMovieData(movie, uiContext);
};
You could try to store the result of the DownloadMovieData function on a temporary variable and add a RunWorkerCompleted event to your background worker to set the actual ObservableCollection to the value of the temporary variable you created.

Tell WPF to evaluate bindings

Say I have a command's execute method that looks like this:
private void Execute()
{
// Bound to a wait indicator in the UI
Searching = true;
// Do some long running process
Thread.Sleep(2000);
Searching = false;
}
When Searching is set to True, the UI element that it is bound to will not update (even though Searching sends the OnPropertyChanged call).
However, once the Thread.Sleep finishes, then the UI then processes my change and shows the wait indicator that Searching is bound to.
Now, I know the conventional wisdom is to make the call to the long running process (Thread.Sleep) be async.
But suppose I am not using the Async libraries, is there a way to instruct WPF to perform the binding on Searching right now?
Something like this:
private void Execute()
{
// Bound to a wait indicator in the UI
Searching = true;
// MADE UP CODE
Binding.UpdateBindingNow("Searching");
// END MADE UP CODE
// Do some long running process
Thread.Sleep(2000);
Searching = false;
}
UPDATE: What I have tried:
Using Dispatcher to try sending the command to the UI Separately.
I also tried the Dispatcher with a high priority.
You need to push your work into a background thread. The bindings won't update until the UI thread can process messages, which won't happen until your operation completes.
Async is one approach, but a more classic approach would be to just use BackgroundWorker to do this, which is supported in all versions of WPF.
If you're using .NET 4, you could do this via the TPL, as well:
private void Execute()
{
// Bound to a wait indicator in the UI
Searching = true;
Task.Factory.StartNew( () => {
// Do long running work...
}).ContinueWith(t =>
{
// You can do work here, including touching UI controls/collections/etc
Searching = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
I agree with others that moving the long-running process to a background/asynch operation is the best way to go. Both because your UI doesn't freeze for the user and also because whatever you do (refresh your binding, whatever) won't actually be reflected in the UI while the long-running process is working because the UI thread will be blocked.
That said, your question is can you force the binding to evaluate/refresh from code, and the answer is yes. You accomplish this by calling UpdateTarget on the BindingExpression like this:
labelTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
There is also an UpdateSource method on the BindingExpression that can be used in much the same way, only impacting the source rather than target.
Again, if your UI thread is blocked, it may not accomplish what you're hoping for, but this is how you would force the binding to re-evaluate.

XamlReader.Load in a Background Thread. Is it possible?

A WPF app has an operation of loading a user control from a separate file using XamlReader.Load() method:
StreamReader mysr = new StreamReader(pathToFile);
DependencyObject rootObject = XamlReader.Load(mysr.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
The process takes some time due to the size of the file, so UI becomes frozen for several seconds.
For keeping the app responsive I try to use a Background thread for performing the part of operation that is not directly involed in UI updating.
When trying to use BackgroundWorker I got an error: The calling thread must be STA, because many UI components require this
So, I went another way:
private Thread _backgroundThread;
_backgroundThread = new Thread(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3 = new StreamReader(path2);
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<StreamReader>)FinishedReading,
mysr3);
}
void FinishedReading(StreamReader stream)
{
DependencyObject rootObject = XamlReader.Load(stream.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
}
This solves nothing because all time consuming operations remain in UI thread.
When I try like this, making all parsing in the background:
private Thread _backgroundThread;
_backgroundThread = new Thread(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3 = new StreamReader(path2);
DependencyObject rootObject3 = XamlReader.Load(mysr3.BaseStream) as DependencyObject;
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<DependencyObject>)FinishedReading,
rootObject3);
}
void FinishedReading(DependencyObject rootObject)
{
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
}
I got an exception: The calling thread cannot access this object because a different thread owns it. (in the loaded UserControl there are other controls present which maybe give the error)
Is there any way to perform this operation in such a way the UI to be responsive?
Getting the XAML to load an a background thread is essentially a non-starter. WPF components have thread affinity and are generally speaking only usable from the threads they are created one. So loading on a background thread will make the UI responsive but create components which then cannot be plugged into the UI thread.
The best option you have here is to break up the XAML file into smaller pieces and incrementally load them in the UI thread making sure to allow for a message pump in between every load operation. Possibly using BeginInvoke on the Dispatcher object to schedule the loads.
As you found out, you can't use XamlReader.Load unless the thread is STA and even if it is, you will have to have it start a message pump and funnel all access to the controls it created through that. This is a fundamental way of how WPF works and you can't go against it.
So your only real options are:
Break down the XAML into smaller pieces.
Start a new STA thread for each Load call. After Load returns, the thread will need to start a message loop and manage the controls it created. Your application will have to take into account the fact that different controls are now owned by different threads.
I don't have exact solution but you can get some direction from following links.
http://www.codehosting.net/blog/BlogEngine/post/Opening-WPF-Windows-on-a-new-thread.aspx
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
System.Xaml has a Xaml​Background​Reader class, perhaps you could get that to work for you. Parser the XAML on the background thread but build the objects on the UI thread.
You can call a method that allows to give control to another thread:
http://msdn.microsoft.com/en-us/library/ms748331.aspx
It is called .Freeze()

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

I have recently started programming in WPF and bumped into the following problem. I don't understand how to use the Dispatcher.Invoke() method. I have experience in threading and I have made a few simple Windows Forms programs where I just used the
Control.CheckForIllegalCrossThreadCalls = false;
Yes I know that is pretty lame but these were simple monitoring applications.
The fact is now I am making a WPF application which retrieves data in the background, I start off a new thread to make the call to retrieve the data (from a webserver), now I want to display it on my WPF form. The thing is, I cannot set any control from this thread. Not even a label or anything. How can this be resolved?
Answer comments:
#Jalfp:
So I use this Dispatcher method in the 'new tread' when I get the data? Or should I make a background worker retrieve the data, put it into a field and start a new thread that waits till this field is filled and call the dispatcher to show the retrieved data into the controls?
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));
japf has answer it correctly. Just in case if you are looking at multi-line actions, you can write as below.
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
Information for other users who want to know about performance:
If your code NEED to be written for high performance, you can first check if the invoke is required by using CheckAccess flag.
if(Application.Current.Dispatcher.CheckAccess())
{
this.progressBar.Value = 50;
}
else
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
}
Note that method CheckAccess() is hidden from Visual Studio 2015 so just write it without expecting intellisense to show it up. Note that CheckAccess has overhead on performance (overhead in few nanoseconds). It's only better when you want to save that microsecond required to perform the 'invoke' at any cost. Also, there is always option to create two methods (on with invoke, and other without) when calling method is sure if it's in UI Thread or not. It's only rarest of rare case when you should be looking at this aspect of dispatcher.
When a thread is executing and you want to execute the main UI thread which is blocked by current thread, then use the below:
current thread:
Dispatcher.CurrentDispatcher.Invoke(MethodName,
new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.
Main UI thread:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background, new Action(() => MethodName(parameter)));
The #japf answer above is working fine and in my case I wanted to change the mouse cursor from a Spinning Wheel back to the normal Arrow once the CEF Browser finished loading the page. In case it can help someone, here is the code:
private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) {
if (!e.IsLoading) {
// set the cursor back to arrow
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => Mouse.OverrideCursor = Cursors.Arrow));
}
}

Categories