Problems with Backgroundworker, GUI is hanged - c#

I am using a background worker below an a form, and when i click a button it should generate a document, but the GUI hangs , i don't know why it does that, because i feel i m using the backgroundworker right.. can anybody helps in this ?
private void btn_GenerateRevDoc_Click(object sender, EventArgs e)
{
DOC_GenerateVersDocBackgroundWorker = new BackgroundWorker();
DOC_GenerateVersDocBackgroundWorker.WorkerReportsProgress = true;
DOC_GenerateVersDocBackgroundWorker.WorkerSupportsCancellation = true;
DOC_GenerateVersDocBackgroundWorker.DoWork += new DoWorkEventHandler(DOC_GenerateVersDocBackgroundWorker_DoWork);
DOC_GenerateVersDocBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(DOC_GenerateVersDocBackgroundWorker_ProgressChanged);
DOC_GenerateVersDocBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DOC_GenerateVersDocBackgroundWorker_RunWorkerCompleted);
System.Threading.Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
if (Db.docVersionHistory != null && Db.docVersionHistory.Count > 0)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "Export Review To";
sfd.Filter = "Word files (*.doc)|*.doc|All files (*.*)|*.*";
sfd.FilterIndex = 1;
sfd.FileName = "";
if (sfd.ShowDialog() == DialogResult.OK)
{
if (!DOC_GenerateVersDocBackgroundWorker.IsBusy)
DOC_GenerateVersDocBackgroundWorker.RunWorkerAsync(sfd.FileName);
}
}
else
{
MessageBox.Show("No Review Records were found!");
}
}
void DOC_GenerateVersDocBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(delegate
{
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}));
}
else
{
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}
}

You don't quite grasp how the BackgroundWorker is supposed to be used, and what the Invoke method does.
The Invoke method causes the code be called on the UI thread. So, don't pass the DocumentsNavigator.GenerateWordRevisionHistoryDoc method through Invoke. RunWorkerAsync is fine. I don't know what types versionsList and Db are, but if they are UI objects, you may need to copy the values you need to a new variable. For example, if versionsList is a ListBox, you should copy the selected values to a new string[], and use that string[] as a parameter to your method.
Here's what you think you wanted to do:
Create new Background worker
initialize your background worker
disable the btn_GenerateRevDoc button
Show the SaveFileDialog
Start the BackgroundWorker (RunWorkerAsync)
On the ProgressChanged event, if you're displaying a progress bar or
something, you can update that, this time you do have to passe it
through the Invoke method.
And on the RunWorkerCompleted event, show a messagebox or something,
and enable the btn_GenerateRevDoc button again
Oh, and this line should absolutely be removed:
System.Threading.Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

Inside your background worker you are again forwarding all the work to UI thread that's why your UI is hanging
if (this.InvokeRequired)
{
//this executes the work on UI thread
Invoke(new MethodInvoker(delegate
{
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}));
}
else
{
//it will also be executed on UI thread
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}
}

when i click a button it should generate a document, but the GUI hangs
from your code I can see that you should enter a file name and click ok. Is there a save dialog being opened somewhere?
Try writing the same code without using the background worker. Does it still hang? Also, observed that the condition if (!DOC_GenerateVersDocBackgroundWorker.IsBusy) doesn't make sense as you are creating a new backgroundworker everytime button is clicked

The problem is that You are running the code that hangs the GUI in a worker thread but you do it in an Invoke method.
The Invoke method runs the code in the thread of the GUI and so it hangs.
If you absolutely must call DocumentsNavigator.GenerateWordRevisionHistoryDoc in the GUI thread I don't see how you can make this call without hanging the GUI.
Try to rethink your code so you don't have to run any code in the BackgroungWorker in an Invoke method.

You are doing something unsafe.
instead of Invoke try
if (this.InvokeRequired)
{ this.BeginInvoke(new MethodInvoker(delegate
{

Related

Dispatcher.BeginInvoke to call another window in c#

In my wpf application i have made a button click event as seperate thread and run as background process so that the UI is responsive to the user. Code as below,
private void btn_convert_Click(object sender, RoutedEventArgs e)
{
//Makes the conversion process as background task which
//makes the UI responsive to the user.
Thread thread = new Thread(new ThreadStart(WorkerMethod));
thread.SetApartmentState(ApartmentState.MTA);
thread.IsBackground = true;
thread.Start();
}
With in the WorkerMethod I have an option to change the filename which i am providing user a separate window.For this action I am using Dispatcher method as below,
if (MessageBox.Show("Do you want to set filename?",
"Information", MessageBoxButton.YesNo, MessageBoxImage.Asterisk) ==
MessageBoxResult.Yes)
{
Action showOutput = () =>
{
BlueBeamConversion.SetOutput _setOutput =
new BlueBeamConversion.SetOutput();
_setOutput.ShowDialog();
};
Dispatcher.BeginInvoke(showOutput);
if (String.IsNullOrEmpty(MainWindow.destinationFileName))
return;
where destinationFileName will be set in SetOutput window. Now come to my issue, when above code executes SetOutput window shows up and doesn't wait until i set the filename. Before setting the filename it comes to the below code,
if (String.IsNullOrEmpty(MainWindow.destinationFileName))
return;
How can i hold until i click ok button in setoutput window.Any suggessions are most welcome.
I used dispatcher.Invoke instead of BeginInvoke. Now it holds the window and takes new name. But when continues the code in workmethod in a certain line it exits the application itself, please fined the code bekow,
if (MessageBox.Show("Do you want to set filename?", "Information", MessageBoxButton.YesNo, MessageBoxImage.Asterisk) == MessageBoxResult.Yes)
{
Action showOutput = () =>
{ BlueBeamConversion.SetOutput _setOutput = new BlueBeamConversion.SetOutput(); _setOutput.ShowDialog(); };
Dispatcher.Invoke(showOutput);
for (int i = 0; i < _listFiles.Items.Count; i++)--- here it exits
{--------- }
Regards
sangeetha
use ShowDialog() instead of Show() and store the output in the DialogResult
var result = _setOutput.ShowDialog();
You can use Invoke instead of BeginInvoke :
//Dispatcher.BeginInvoke(showOutput);
Dispatcher.Invoke(showOutput);
if you use ShowDialog, you can store the value in a public property of your second window and can access it in a way like this:
Form2 form2 = new Form2();
if (form2.ShowDialog() == DialogResult.OK)
{
if (form2.ReturnData == "myResult")
...
}
while you are using window.show() method in you action you will not receive any result from show method insteed you have to call the show dialog method of window which will inforce the GUI to hold untill the dialog window is closed and after it you will be able to recive the data from you dialog windo.
Action showOutput = () =>
{ BlueBeamConversion.SetOutput _setOutput = new BlueBeamConversion.SetOutput(); _setOutput.ShowDialog(); };
Dispatcher.BeginInvoke(showOutput);
and the other hand you can wait for the thread to be complete first and till you can wait. this approch will also work for you. the dispatcher.Invoke will help you out.or you can try DispatcherOperation here.
try with below changed code.
DispatcherOperation op = Dispatcher.BeginInvoke(showOutput);
op.Wait();
if (String.IsNullOrEmpty(output))
return;

Best way to display a progress form while a method is executing code?

I have a WinForm load method that takes a long time to gather some data to display to the user.
I display a form with a large font with the word "Loading" while this method is executing.
However sometimes this error comes up and the "Loading" progress form does not close and then eventually my whole application will just exit:
Error creating window handle. at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
Is there a better way to display my progress/loading form while I am executing code in the load method?
This is my code:
//I launch a thread here so that way the Progress_form will display to the user
//while the Load method is still executing code. I can not use .ShowDialog here
//or it will block.
//Progress_form displays the "Loading" form
Thread t = new Thread(new ThreadStart(Progress_form));
t.SetApartmentState(System.Threading.ApartmentState.STA);
t.IsBackground = true;
t.Start();
//This is where all the code is that gets the data from the database. This could
//take upwards of 20+ seconds.
//Now I want to close the form because I am at the end of the Load Method
try
{
//abort the Progress_form thread (close the form)
t.Abort();
//t.Interrupt();
}
catch (Exception)
{
}
A BackgroundWorker is a great way to perform a long running operation without locking the UI thread.
Use the following code to start a BackgroundWorker and display a loading form.
// Configure a BackgroundWorker to perform your long running operation.
BackgroundWorker bg = new BackgroundWorker()
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
// Start the worker.
bg.RunWorkerAsync();
// Display the loading form.
loadingForm = new loadingForm();
loadingForm.ShowDialog();
This will cause the following method to be executed on a background thread. Note that you cannot manipulate the UI from this thread. Attempting to do so will result in an exception.
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
// Perform your long running operation here.
// If you need to pass results on to the next
// stage you can do so by assigning a value
// to e.Result.
}
When the long running operation completes, this method will be called on the UI thread. You can now safely update any UI controls. In your example, you would want to close the loading form.
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Retrieve the result pass from bg_DoWork() if any.
// Note, you may need to cast it to the desired data type.
object result = e.Result;
// Close the loading form.
loadingForm.Close();
// Update any other UI controls that may need to be updated.
}
Ive successfully tested this on .NET 4.0. (WinForms) I'm reasonably certain that this will work on .NET 4.0+ and should be a useful code snippet to reuse in most of your projects that require closing forms at the end of a process.
private void SomeFormObject_Click(object sender, EventArgs e)
{
myWait = new YourProgressForm();//YourProgressForm is a WinForm Object
myProcess = new Thread(doStuffOnThread);
myProcess.Start();
myWait.ShowDialog(this);
}
private void doStuffOnThread()
{
try
{
//....
//What ever process you want to do here ....
//....
if (myWait.InvokeRequired) {
myWait.BeginInvoke( (MethodInvoker) delegate() { closeWaitForm(); } );
}
else
{
myWait.Close();//Fault tolerance this code should never be executed
}
}
catch(Exception ex) {
string exc = ex.Message;//Fault tolerance this code should never be executed
}
}
private void closeWaitForm() {
myWait.Close();
MessageBox.Show("Your Process Is Complete");
}
I would take the code that you have in your load method and place that into a thread. Setup a progress bar somewhere on your form and increment it at key stages in the code that's gathering the data - be careful not to do this in the thread itself though, i.e. don't tamper with ui elements in a separate thread, you'll need to invoke them using a delegate.

DotNetZip freezing my application?

string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
public void unzip(String zFile)
{
Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(zFile);
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(zip_ExtractProgress);
zip.ExtractAll(desktop + "\\cache", ExtractExistingFileAction.OverwriteSilently);
zip.Dispose();
zip = null;
}
void zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten)
{
label2.Text = "debug: " + ((e.EntriesExtracted));
}
else if (e.EventType == ZipProgressEventType.Extracting_BeforeExtractEntry)
{
label3.Text = e.CurrentEntry.FileName;
}
}
private void button1_Click(object sender, EventArgs e)
{
unzip(desktop + "\\cache.zip");
}
When I execute the unzip button1_Click() my application freezes. I'm new to C# and I'm not really sure how to fix this, can someone help me?
Long running blocking operations should not run on the main UI thread, since as you can see the UI will freeze up.
Consider using a BackgroundWorker to do the work on a separate thread.
There's a good summary here.
Report progress back to the UI by handling the ProgressChanged event and calling backgroundWorker.ReportProgress(), rather than directly updating label2.Text from inside there.
i.e. from inside your zip_ExtractProgress method, call backgroundWorker.ReportProgress
label3.Text = e.CurrentEntry.FileName;
label3.Update();
The Update() method ensures that the label is painted, now showing the Text property you assigned. Without it, the painting doesn't happen until the unzipping code stops running and your program goes idle again. Otherwise known as 'pumping the message loop'. Calling Update() is only a partial fix, your window is still catatonic and won't respond to mouse clicks for example. If it takes longer than a couple of seconds, Windows displays the "Not responding" ghost window.
Get some experience with coding in C#, then tackle threading with the BackgroundWorker class.
Easiest way: Execute the unzip method using a BackgroundWorker. Be sure to modify GUI controls only on the main GUI thread, by using Invoke.

Can BeginInvoke interrupt code already running on the UI thread?

Suppose I have some code which is running in the UI thread, which spawns a new thread to do work in the background, and then goes on to do UI work. When the background work is done, BeginInvoke is called from the new thread. If the UI work is still going on, will the callback given to BeginInvoke interrupt the UI work, or will it wait?
Code example - add a button called button1 to a form and add button1_Click to its click event, and then add this to the form:
bool continueWork = true;
private void button1_Click(object sender, EventArgs e)
{
Thread workThread = new Thread(performBackgroundWork);
workThread.IsBackground = true;
workThread.SetApartmentState(ApartmentState.STA);
workThread.Start();
for (long i = 0; i < long.MaxValue; i++)
{
if (!continueWork)
break;
button1.Text = i.ToString();
}
}
private void performBackgroundWork()
{
Thread.Sleep(1);
button1.BeginInvoke(new MethodInvoker(stopUIWork));
}
private void stopUIWork()
{
continueWork = false;
}
What is button1's text after it is clicked?
BeginInvoke adds the delegate to a queue (the message queue to be exact). So the answer is no, they won't get interrupted. Your button click handler is in fact executed due to a message in the message queue as well.
EDIT: Sorry, I forgot to answer your second question, but you can probably figure that out by yourself. The button text will be long.MaxValue - 1. You would probably want to execute stopUIWork without using BeginInvoke.

Load Data and show a splash screen during that

I got to load some data out of a db4o database which takes 1 or 2 seconds at the startup of my app, the rest has to wait because first of all the data has to be loaded. doing this in an own thread would mean that the rest has to wait for the thread-finishing. I'd like to do a splash screen or something during the data is loaded for what also need an own thread, right? how would you do?
I'm using csharp, .net 3.5 and winforms
Showing a splash screen at startup is easy to do. In your application's Main() method (in Program.cs), put something like this before the Application.Run(...) line:
SplashForm splashy = new SplashForm();
splashy.Show();
Application.Run(new MainForm(splashy));
Modify the code and constructor for your main form so that it looks something like this:
private SplashForm _splashy;
public MainForm(SplashForm splashy)
{
_splashy = splashy;
InitializeComponent();
}
Then at the end of your MainForm's Load event (which presumably contains the database code), put this code:
_splashy.Close();
_splashy.Dispose();
If you choose to do your database access with a separate Thread or BackgroundWorker, then you don't really need a splash screen so much as you need some sort of progress indicator form that appears while the BackgroundWorker is doing its thing. That would be done differently from my answer here.
One way, probably better ways though. Create a new dialog form that will be your progress window/splash screen. Throw a bitmap or whatever on it as the only item. Instantiate the dialog from your main program. Override the Load event for the progress form and from there launch the new thread that will do the background processing work for loading up the data. This way you can just call ShowDialog from your main app.
if you use System.ComponentModel.BackgroundWorker then you can easily wire up events for when the thread completes and automaticaly exit the dialog from that event. Control is returned back to the calling application and you're done.
I've done this sort of thing in an application before and it works fine but I'm sure it's a novice approach. Here's sample code from the Load event in the form that launches the background thread (in my case I'm opening and parsing large files):
private void FileThreadStatusDialog_Load(object sender, EventArgs e)
{
Cursor = Cursors.WaitCursor;
if (m_OpenMode)
{
this.Text = "Opening...";
StatusText.Text = m_FileName;
FileThread = new BackgroundWorker();
FileThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FileThread_RunWorkerCompleted);
FileThread.DoWork += new DoWorkEventHandler(FileOpenThread_DoWork);
FileThread.WorkerSupportsCancellation = false;
FileThread.RunWorkerAsync();
}
else
{
this.Text = "Saving...";
StatusText.Text = m_FileName;
FileThread = new BackgroundWorker();
FileThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FileThread_RunWorkerCompleted);
FileThread.DoWork += new DoWorkEventHandler(FileSaveThread_DoWork);
FileThread.WorkerSupportsCancellation = false;
FileThread.RunWorkerAsync();
}
}
And here's what the work completed method looks like which exist the form:
private void FileThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
FileThread = null;
DialogResult = DialogResult.OK;
Close();
}
Here's how I open up the progress dialog from the main dialog:
FileThreadStatusDialog thread = new FileThreadStatusDialog(m_Engine, dlg.FileName, true);
if (thread.ShowDialog(this) == DialogResult.OK)
{
m_Engine = thread.Engine;
FillTree();
}
One might want to force drawing of splashy in MusiGenesis' answer by adding
Application.DoEvents();
immediately after
splashy.Show();

Categories

Resources