i'm loading screen in backgroundworker:
private void LSLoadingScreen(object sender, DoWorkEventArgs e)
{
LoadingScreen ls = new LoadingScreen(this.timerStart);
ls.ShowDialog();
while (LoadingScreen.CancellationPending)
{
ls.Dispose();
LoadingScreen.Dispose();
}
but my loadingScreen doesn't dispose when i use this code in other function:
LoadingScreen.CancelAsync();
timerStart = false;
LoadingScreen.Dispose();
How to dispose it properly?
Firstly, ShowDialog() will prevent the rest of the code executing until the dialog is closed - which you are never doing.
Even when it does close, it will evaluate the while loop (which will most likely be false so skipped) and then your backgroundworker will be finished.
If all you are doing is showing a dialog then I would just do this on the main thread, and have your loading process on the background worker..
Fire background worker (which does loading code)
Show your loading dialog
On BackgroundWorkerCompleted event, close your loading dialog
Try to get all your UI elements in the main UI thread.
Hope that helps
EDIT:
Based on your comment...
public partial class MainForm:Form
{
LoadingScreen ls;
public MainForm()
{
}
public void StartLoad()
{
ls = new LoadingScreen(this.timerStart);
backgroundWorker.RunWorkerAsync();
ls.Show();
}
void backgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
//Loading code goes here
}
void BackgroundWorkerMainRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(ls != null)
ls.Close();
}
}
Related
In a C# desktop application, a backgroundworker responsible for saving application state is being called in 2 situations. Once while the application is running. That works fine. Other when application is being closed, backgroundworker is called to save the application state but before it starts saving, the application is closed and nothing gets saved.
I tried to solve it by using the AutoReset event class in DoWork and RunWorkerCompleted but didnt work because application closed before backgroundworker could save any thing.
Question is - how can I make the main thread wait until backgroundworker finishes saving?
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
this.saveAHToolStripMenuItem_Click(this, e);
}
private void saveAHAsToolStripMenuItem_Click(object sender, EventArgs e)
{
this.backgroundWorkerMain1.RunWorkerAsync(args);
}
private void backgroundWorkerMain1_DoWork(object sender, DoWorkEventArgs e)
{
saveMethod();
}
private void backgroundWorkerMain1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
showResultOfSaving();
}
Is it WinForms?
Maybe you could register to the OnClosing event.
Within it, set a private property IsClosing to true.
Mark the eventhandler e as e.Handled = true.
Register to the BackgroundWorker event RunWorkerCompleted. Within it, check if the IsClosing property is set and in that case MainForm.Close() the application.
Edit:
using System;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;
namespace BgWorker
{
public partial class Form1 : Form
{
BackgroundWorker _bgWorker;
bool _iNeedToCloseAfterBgWorker;
public Form1()
{
InitializeComponent();
}
void Form1_Load(object sender, EventArgs e)
{
_bgWorker = new BackgroundWorker();
_bgWorker.DoWork += _bgWorker_DoWork;
_bgWorker.RunWorkerCompleted += _bgWorker_RunWorkerCompleted;
}
void _bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done!");
if (_iNeedToCloseAfterBgWorker)
Close();
}
void _bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do long lasting work
Thread.Sleep(2000);
}
void btnWorkIt_Click(object sender, EventArgs e)
{
// Note how the Form remains accessible
_bgWorker.RunWorkerAsync();
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_iNeedToCloseAfterBgWorker || _bgWorker.IsBusy) return;
e.Cancel = true;
_iNeedToCloseAfterBgWorker = true;
_bgWorker.RunWorkerAsync();
}
}
}
It would be cleaner to place the SaveState code in a separate method, which is called from the background worker and (synchronously) when you close the application.
Either that, or you can block the application from closing based on a manual reset event (ManualResetEvent-class).
Just throwing an idea out there.
Perhaps a volatile DateTime property on the MainForm.
(Note - DateTimes cannot be volatile it appears, so you could use the string representation instead)
public volatile string _lastUpdated;
This property gets updated everytime the save event is carried out on the background worker thread.
OnClose will check the Time Difference between DateTime.Now and the stored date time.
Eg:
var timeDiff = DateTime.Now - _lastSavedTime;
If the OnClose detects that the timeDiff.TotalSeconds is < 30 (less than 30 seconds)
You can then trigger the save event manually from the main thread, prior to the close event being completed.
This however won't protect against Process.Kill - very little can protect against that.
All I can suggest is that you manage your saves in a smart way.
Eg: Save to a new File each time, keeping the last 5 saves.
When the 6th save is made, delete the oldest save etc
This is to account for the potential corruption that may happen from a Process.Kill scenario. Means you will still have at least a 60 second backup, in case the 30 second backup fails.
My solution to this problem is to wait untill backgroundworker is fnished by adding following after backgroundworker async call.
while (this.backgroundWorkerMain1.IsBusy)
{
Application.DoEvents();
}
I have a C# windows application in which I create an instance of a some class, in which it has a member which is a window form and this form has a button that when I click it I open a new form that can run a worker thread, let's say:
public static void Main()
{
MyClass mc = new MyClass();
mc.ShowForm();
}
in MyClass.cs:
public void ShowForm()
{
MyFirstForm firstForm = new MyFirstForm();
firstForm.Show();
}
in MyFirstForm.cs:
private void myButton_Click(object sender, EventArgs e)
{
MySecondForm secondForm = new MySecondForm();
secondForm.Show();
}
in MySecondForm.cs:
private void startButton_Click(object sender, EventArgs e)
{
var worker = new Thread(StartWork);
worker.Start();
}
private void stopButton_Click(object sender, EventArgs e)
{
m_stopped = true;
}
private void StartWork()
{
while(!m_stopped)
{
//work...
}
}
When I run my app, clicks myButton, then click startButton, and then exit my app (I have a tray Icon that when clicked to exit, call base.Shutdown() ), the second form stays hanging and non responsive.
My original question was, what is the best way to notify the second form that it should close and stop the running thread, but during writing this post I noticed that I can use Task.Factory.StartNew(StartWork); and when I tried it, it worked without a hitch.
So now I have another question which is why is this happening?
I tried registering to Closing , Closed , FormClosing events and setting m_stopped = true but they were not getting called.
EDIT:
As #drf suggested the thread is a foreground thread hence the app is blocked while it runs, so adding:
worker.IsBackground = true;
fixed it.
Your thread is currently a foreground thread which will prevent the process from exiting until the thread finishes. http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx
Change your thread startup code to this:
private void startButton_Click(object sender, EventArgs e)
{
var worker = new Thread(StartWork);
worker.IsBackground = true;
worker.Start();
}
I think for closing application you should use Application.Exit Method:
It informs all message pumps that they must terminate, and then closes all application windows after the messages have been processed.
Also you could track FormClosing event of each form. Check if Thread.IsAlive then Thread.Abort() or cancel closing.
I have an application in which I launch a window that displays byte data coming in from a 3rd party tool. I have included .CancelAsync() and .CancellationPending into my code (see below) but I have another issue that I am running into.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Thread popupwindow = new Thread(() => test());
popupwindow.Start(); // start test script
if(backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
}
}
private voide window_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
this.backgroundWorker.CancelAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
Upon cancelling the test I get an `InvalidOperationException occurred" error from my rich text box in my pop-up window. It states that "Invoke or BeginInvoke" cannot be called on a control until the window handle has been created". I am not entirely sure what that means and would appreciate your help.
LogWindow code for Rich Text Box:
public void LogWindowText(LogMsgType msgtype, string msgIn)
{
rtbSerialNumberValue.Invoke(new EventHandler(delegate
{
rtbWindow.SelectedText = string.Empty;
rtbWindow.SelectionFont = new Font(rtbWindow.SelectionFont, FontStyle.Bold);
rtbWindow.SelectionColor = LogMsgTypeColor[(int)msgtype];
rtbWindow.AppendText(msgIn);
rtbWindow.ScrollToCaret();
}));
}
After reading your code; it appears the background worker completes nearly instantly; likely killing any threads that were spawned from it. More to the point; a background worker that is already stopped will throw "InvalidOperationException" when "CancelAsync" is called.
I'd advise placing any GUI related work into caller instead of the background worker thread. This is an important consideration because you will get cross-thread exceptions and other strange behavior such as rather serious GUI refresh issues.
The background worker "DoWork" method should be considered threaded already. You can see this by adding simple debug statements to your code.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
private void button1_Click(object sender, EventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
backgroundWorker1.RunWorkerAsync();
}
Finally; I'd add that CancellationPending works best when polled in a loop-construct like so,
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var workItem in work)
{
workItem.Perform();
if (backgroundWorker1.CancellationPending)
{
break;
}
}
}
Hope this helps.
What's happening is the window handle is already gone before the LogWindowText method is called by the finalize method (RunWorkerCompleted handler) of the background worker. You need to check that Handle:
if (this.Handle == IntPtr.Zero) { return; }
I have an application that when busy will open a busy form (FormWaitingForm) to indicate to the user that the application is busy. How do i close FormWaitingForm in the event backgroundWorker1_RunWorkerCompletedbelow ?
private void radButtonCheckFiles_Click(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
// define the event handlers
bw.DoWork += new DoWorkEventHandler(ProcessTickTemp);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
bw.RunWorkerAsync(); // starts the background worker
// execution continues here in parallel to the background worker
using (var FormWaitingForm = new WaitingForm()) //
{
var result = FormWaitingForm.ShowDialog();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// How do i close FormWaitingForm here ?
}
You could try something like this. Retain a reference to the form outside of the click method and then open it non-modally (so that you don't have to wait for the user to close it).
WaitingForm formWaitingForm;
private void radButtonCheckFiles_Click(object sender, EventArgs e)
{
// background code here
formWaitingForm = new WaitingForm();
formWaitingForm.Show();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
formWaitingForm.Close();
formWaitingForm.Dispose();
}
You would have to add some code to handle if the user closes the waiting form without waiting for you to do it.
That said, the way I usually implement a waiting/progress-type form is to incorporate the background process into the progress form itself and show something like a progress bar.
This link might give you some more ideas.
I am using Windows Form application for my thread demo. When I click on button1 ,It will start the thread and recursively doing a work.
Here the Form will not hang as I expected. I want to Stop the currently running thread when I click on Button2. However this won't work.
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(doWork); // Kick off a new thread
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
t.Abort();
}
static void doWork()
{
while (true)
{
//My work
}
}
}
.When Im debugging, the button2_Click method won't hit the pointer. I think because Thread is keep busy.
Please correct me if I going wrong somewhere.
You can't kill thread like this. The reason is to avoid situations where you add lock in thread and then kill it before lock is released.
You can create global variable and control your thread using it.
Simple sample:
private volatile bool m_StopThread;
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(doWork); // Kick off a new thread
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
m_StopThread = true;
}
static void doWork()
{
while (!m_StopThread)
{
//My work
}
}