I have a main thread and a child thread. The child thread is sta and updates the ui. The main thread does calculations. I am using the following code in the sta thread:
m.Dispatcher.Invoke(() =>
{
m.ProgressText.Content = newPrText;
m.ProgressBar.Value += prInc;
m.ProgressBar.Maximum = prMax;
});
The UI just freezes and hangs. It does not update the fields.
You should switch it around: Let the main thread update the UI and the child thread perform the calculations. Most likely, the controls are created on the main thread, so the dispatcher will marshal the updates back to the main thread, effectively performing all work on the main thread.
Related
For numerous reasons i cannot run the main portion of the code in a background worker, i can instead run a Loading dialog in the background worker but need to close the dialog after the execution of the main code portion. I've implimented the following but I'm not sure how to forcefully close the background worker after the code execution:
LoaderWorker = new BackgroundWorker();
//Loading is the Form
Loading Loader = new Loading("Daten Exportieren");
LoaderWorker.DoWork += (s, args) =>
{
//Show Loading Dialog
Loader.Show();
};
ExecuteMainTasks();
//Here i need to stop the backgroundworker after the method above is complete.
Is there a solution to this with background worker or should i use a different approach to ensure i do not lock the UI thread for the loader. Remember...i cannot execute the main code in the backgroundworker so have to manage this somewhat backwards.
Thanks.
Control.Invoke
Control.BeginInvoke
(Windows Forms)
or
Disopatcher.Invoke
Dispatcher.BeginInvoke
(WPF)
or
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync
(WinRT 😂)
Calling this from the background worker will execute the passed lambda / delegate in the UI thread. Simply perform progress / finished notifications this way from your background worker.
So, show your dialog in the UI thread, then start the background worker, from which you can use this way of executing code in the UI thread.
Just make sure that you synchronously stop the background worker when the user cacels / closes the dialog.
I'm using a WPF CollectionView and I'm setting the Filter in a background thread, because it takes a long time to apply this filter.
Setting this Filter triggers the method ScheduleMapCleanup() of CollectionView (so WPF framework code I can't change). In this method, Dispatcher.CurrentDispatcher.BeginInvoke is used.
However, because this is executed in a background thread, this Action is never executed (the Dispatcher of this thread is never started), causing a memory leak: The Dispatcher keeps a reference to the CollectionView.
How could I work around this problem? Setting the Filter in the UI thread is not an option.
Could I start the Dispatcher myself? If so, how do I do this (Dispatcher.Run halts everything)?
I use this when I need to update some controls and binding on my UI thread from my background tasks:
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Loaded,
new Action(() => {
// Code here
})
);
If it's not this can you be more specific on what you want to do on your UI thread
Accessing the current dispatcher from a background thread does not give you the UI dispatcher, it gives you a new one for the background thread.
Either call CurrentDispatcher from the foreground thread and pass the result to the background thread, or call DependencyObject.Dispatcher to get the dispatcher for a window or other control.
Edit: I just read the question more closely. Since you do not control the code calling CurrentDispatcher, the only way that it will work is to call that code from the UI thread.
To be clear: I don't use Dispatcher.CurrentDispatcher in my code. This is used in the WPF framework code, so I can't change this.
This code is executed in a background thread because I'm setting the Filter in a background thread. I'm setting this property in a background thread because it can take up to several minutes. Setting it in a background thread keeps the UI responsive and lets me show a loading indication to the user.
I fixed the memory leak (caused by the not-running background Dispatcher keeping a reference to the CollectionView) by adding a Shutdown to the Dispatcher and starting the dispatcher in the background thread:
//All code below is executed on a background thread
//Line below causes WPF framework to add something to Dispatcher.CurrentDispatcher queue.
view.Filter = new Predicate<Object>(actionTarget.FilterCallback);
if (Thread.CurrentThread.IsBackground && Dispatcher.CurrentDispatcher != Application.Current.Dispatcher)
{
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
Dispatcher.Run();
}
If the background thread is reused later (for example because it's a thread pool thread, started by a BackgroundWorker) you can't use BeginInvokeShutdown like in the code above: a shut down dispatcher can not be started again. In that case, use this instead of the BeginInvokeShutdown:
Dispatcher.CurrentDispatcher.BeginInvoke((Action) delegate() { Dispatcher.ExitAllFrames(); }, DispatcherPriority.Background);
This will make sure the Run() method returns, but the dispatcher can be started again later on.
Edit: As Mitch mentioned in comment below, be carefull when multiple threads can be executing the Run() at the same time. If necessary add a lock around the Run().
I have an ObservableCollection that is declared at the class level. This is a collection of custom controls.
private ObservableCollection<AssetCriticalDimensionItemDetail> _details;
private Dispatcher UIDispatcher;
A method is called that instantiates a new ObservableCollection on the UI thread. I'm also setting the UI dispatcher at this time.
At this point in the code, everything is done synchronously. I captured the thread ID so I can compare it when I'm modifying the collection.
After this is done, some controls are added to a different collection and then a background thread runs. Once the background thread is done, the user can enter some options in the child control and click "Add." Adding fires an event which calls back to the parent control and modifies the collection.
You can see from that image that the thread ID is the same as the UI thread, but it won't allow me to modify the collection even though it was created on the UI thread. So I tried calling UIDspatcher.Invoke, but it still gives me the same error.
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
Sounds like you may be creating controls on a background thread, then showing them on the main UI thread. Alternatively, controls created on the UI thread may be interacting with Dispatcher-affine objects created on a background thread. In either case, that would be your problem: any object inheriting from DispatcherObject is irrevocably bound to the thread on which it was created.
#MikeStrobel is that even possible? AFAIK controls check during creation if they are created on the UI thread
Yes, there is no such thing as "the UI thread" as far as the framework is concerned. Your application could, in theory, have multiple threads running WPF dispatchers. The constructor for DispatcherObject initializes its Dispatcher property to Dispatcher.CurrentDispatcher, which creates a Dispatcher for the current thread if one does not already exist (though the newly created Dispatcher, if created, will not actually be running).
I want to show a new form with a progress bar marquee. This form should be on an other thread, since the action that is called is too big and the UI doesn't update the progress bar if it's on the same thread.
I call a new thread, create my form, but as soon as it appears, the form close...
Here is my code from my main :
frmProgress f = new frmProgress();
Thread t = new Thread(new ThreadStart(delegate() { f.Show(); }));
t.Start();
documentManager.Compile(); //This is my big action
t.Abort();
Here is the code of form :
public frmProgress()
{
InitializeComponent();
}
I would like to know what I did wrong.
Thanks!
You have several problems:
You're showing a form in a non-UI thread. This will only cause problems; it needs to be in the UI thread.
You're performing a long running task in the UI thread; this is blocking UI updates.
You need to be showing the forum in the UI thread, and running the long running task in a background thread.
A great tool for the job here is to use a BackgroundWorker. It will do much of the work for you to ensure the proper code is running in the UI/non-UI threads as it should be. Create a new BackgroundWorker and put the long running task in the DoWork event. Make any simple UI updates that you need to (showing the form) before you start the BGW, and do any cleanup in the Completed event (which will run in the UI). The MSDN page on BackgroundWorker has some nice examples.
Another advantage of using a BackgroundWorker is that you can easily report progress (if you have a good way of knowing your % complete) rather than just having a marquee bar. Just add a handler to the ProgressReported event handler to update the progress bar, and call the ReportProgress method periodically when doing work. See the linked examples for exact code snippets.
I have a performance problem with my application, when I load a TabItem (each TabItem is bound to a ViewModel, each ViewModel having a DataTemplate). To solve this problem, I use an asynchronous loading in ViewModel constructor :
public MyViewModel(MyObject entity)
{
// WpfContext it's my Dispatcher
Task.Factory.StartNew(() => WpfContext.Invoke(() =>
{
//Initialisation
LoadMyObject(entity);
}));
}
With this solution, the first time the TabItem is loaded, it takes some times and seems not really asynchronous. For the other loads, it works good and asynchronously. I don't know exactly why. Any suggestion ?
Invoke on a Dispatcher is a blocking call for both the background thread of your Task and then also the UI thread (once it decides to actually run your code).
It seems asynchronous sometimes because the UI thread is busy showing the new Tab, and so the Invoke from the background thread is blocking until the UI thread has time to process it. When it seems like it is synchronous, the Invoke call is being processed by the UI thread before the new Tab is being displayed. So, in the end, I think you have a race condition.
To resolve this, you may need to refactor your LoadMyObject method so it can be run on the background thread, or you could use the Dispatcher.BeginInvoke method and provide it a lower priority to ensure the display of your new Tab precedes the processing of the LoadMyObject call