Loading Dialog in BackgroundWorker - c#

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.

Related

WPF UI won't update even though I am using a dispatcher

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.

WPF framework using Dispatcher.CurrentDispatcher in background thread, causing memory leak

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().

Hosting multiple instance of app in a tabcontrol/ queuing

So I have This app with a MainForm that has a few buttons on it. and the buttons would do time consuming tasks that includes working with MainForm UI. Because some times we need to run multiple instances of this app at the sametime I decided to Create a MainFormHost where it is a form with a tab control which under each tab I create an instance of my MainForm and host it there. And so far everything was ok. The problem is when I click on a button on MAinForm1 it starts working fine but as soon as I click on a button on MainForm2 the process of MainForm1 button gets queued behind the MainForm2 process.
MainForm GetMainFrom(TabPage tabPage)
{
tabPage.Invoke(new Action(() =>
{
mainForm = new MainForm();
mainForm.TopLevel = false;
mainForm.FormBorderStyle = FormBorderStyle.None;
mainForm.Dock = DockStyle.Fill;
_mainForms.Add(mainForm);
tabPage.Controls.Add(mainForm);
mainForm.Show();
}));
}
And then call the method:
var mainFormThread = new Thread(() =>
{
mainForm = GetMainFrom(tabPage);
});
mainFormThread.SetApartmentState(ApartmentState.STA);
mainFormThread.Start();
I cannot change the code inside the MainForm to Invoke things when they want to work with the UI because it is going to be too much of work but I can easily make each button click to be called from a thread/task or...
TIA
In short, you can only have one 'main thread' that can interact with the UI controls. Ever. Period. There is no way to get around this (in standard code, you can draw to your form from a different process, but I won't go into that)
So, what you need to do is arrange your code in a set pattern.
Methods that do background work - this can be done on a separate thread(s)
Methods that Update the UI - MUST be done on the UI/Main Thread
The idea being simple, background work takes a long time, and updating the UI shouldn't.
Because you have 1 or 2 long running actions running ON THE MAIN UI THREAD, this 'blocks the UI' and causes the behavior your are experiencing.
By blocks the UI, what you actually have is the Windows Message pump can't pump. So every single command to resize a window or update a control on a form is a message in the pump. That must run on the main thread. If you do 'work' on that thread, such as connecting to a database, download a file etc., then the pump can't continue, so you get the 'this application is not responding' message.
So, you must download the file or whatever on a thread, and when it's finished, transition to the UI thread, and update the UI, e.g. say finished in a text box.
The way to transition between the two threads (background to main) is to use begininvoke, and you know if this is needed by using 'invoke required' .
From MSDN
// This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control.
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly.
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
This is a great article and will get you where you need to be:
MSDN about thread safe code and invoke required

Progress dialog on a heavy loaded UI thread does not get updated when binding changes

While maintaining an old MFC application we have implemented a new progress dialog bar in WPF. The application currently has the UI thread busy with a lot of business operations but changing this is out of scope.
When a string property changes its value (binded to the text of a TextBox) the progress dialog does not get refreshed (only sometimes when the thread is not so busy).
As far as I know as the update of the property is done from the UI thread the thread should be able to update the dialog and repaint it before going on the next thing so I don't get why it's not being updated and how to fix it.
Any ideas?
EDIT: What are the drawbacks of this solution, I have tried it and seems to work fine:
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
I found out the solution here:
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
Then I created a new thread with the progress dialog.
The application currently has the UI thread busy with a lot of business operations
Well there's your problem. You shouldn't be doing that. You ought to be performing long running operations in a non-UI thread. It's the reason why updates to the UI aren't made until after the long running operation finishes.
You can use a BackgroundWorker to help simplify interactions with a UI while performing a long running task, as it will handle marshaling to the UI thread for the progress updated and completed event handlers.
First, I agree with #Servy, you shouldn't do heavy work in the UI thread.
However, if you cannot do the work in another thread, you can consider spawning another one for your dialog. I don't know how you are calling the wpf window, but this link may give you some clues about how it would be done in C#.

multithread : new form close too fast

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.

Categories

Resources