Using the following code my background worker RunWorkerCompleted is never called and I can't figure out why.
void startWaitScan()
{
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// do something here
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("BackgroundWorker1_RunWorkerCompleted");
if (!stopAsync)
{
backgroundWorker1.RunWorkerAsync();
}
}
My goal is for the background worker to run continuously, I had this working in .NET but when I rewrote the code now in C# I'm having this issue.
Do you have all the events hooked up correctly ??
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
Related
I'm doing a simple login form that will show the login process after they click button. It will verify the user access rights and give true or false.
I got an error on this.Hide();
System.InvalidOperationException: 'Cross-thread operation not valid: Control 'formLogin' accessed from a thread other than the thread it was created on.'
Any help would be greatly appreciated. Thanks!!
private void btnLogin_Click(object sender, EventArgs e)
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
PBLogin.Style = ProgressBarStyle.Marquee;
PBLogin.MarqueeAnimationSpeed = 50;
bgw.RunWorkerAsync();
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
PBLogin.Style = ProgressBarStyle.Blocks;
PBLogin.MarqueeAnimationSpeed = 0;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
if (CheckAuthorization())
{
MessageBox.Show("Login Successfully");
TestScript next = new TestScript();
next.Show();
this.Hide();
}
else
{
MessageBox.Show("Login Failed");
}
From a background thread you can't reach the UI, that's what the exception is trying to tell you. In your background thread you should try to focus only on the Authorization logic itself. Try to avoid UI component manipulation.
In the RunWorkerCompleted event you can indeed modify the UI components. So, do the calculation on a dedicated background thread and do the UI manipulation inside the completion event.
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//TODO: Check against e.Error and e.Cancelled before you try to access e.Result
if((bool)e.Result)
{
//UI modification
}
else
{
//UI modification
}
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
var isAuthorized = CheckAuthorization();
e.Result = isAuthorized;
}
Unfortunately I was not able to find relevant answer to my problem. I have a object encoder that has an event "VideoEncoding". It passes custom EncodingEventArgs that include various Properties like Progress, Size etc. I can output this info to Console or write to text file. But when I try to utilize it in WinForms I'm not able to pass that information to UI like label or progress bar. I tried different approaches. Background Worker seems like a good idea, The problem is that Background Worker cannot subscribe to VideoEncoding event, neither it will take my custom EventArgs. This is what i was able to put together. Maybe there is a different way to do it using delegates that would communicate with UI. Any suggestions are welcome. Thank you.
public partial class Form1 : Form
{
private BackgroundWorker bw;
int _progress;
public Form1()
{
InitializeComponent();
this.bw = new BackgroundWorker();
this.bw.DoWork += new DoWorkEventHandler(bw_DoWork);
this.bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
this.bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
this.bw.WorkerReportsProgress = true;
this.button1.Click += new EventHandler(button1_Click);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.label1.Text = "The job is: " + e.Result.ToString();
this.button1.Enabled = true;
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.label2.Text = e.ProgressPercentage.ToString() + "% complete";
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
this.Encode
worker.ReportProgress(_progress);
e.Result = "Completed";
}
private void button1_Click(object sender, EventArgs e)
{
if (!this.bw.IsBusy)
{
this.bw.RunWorkerAsync();
this.button1.Enabled = false;
}
}
public void Encode()
{
var job = new EncodingJob();
//setup encoding job
//subscribe to an event
ffmpeg.VideoEncoding += GetProgress;
ffmpeg.DoWork(job);
}
public void GetProgress(object sender, EncodingEventArgs e)
{
_progress = (int)e.Progress;
}
}
Try to call the background workers ReportProgress in the GetProgress Method. How should the form know your progress if you don't signalize it?
Is it possible to add a callback to a background worker while it is running ?
bw.DoWork += new DoWorkEventHandler( some callback );
bw.RunWorkerAsync();
bw.DoWork += new DoWorkEventHandler( some callback );
Thank you.
Yes you can as it's only a subscription to an event but you can't run bw until he has completed the execution of the first task
here an example to illustrate this the following code will show an InvalidOperationException telling This BackgroundWorker is currently busy and cannot run multiple tasks concurrently."
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync();
backgroundWorker1.DoWork+=new DoWorkEventHandler(backgroundWorker2_DoWork);
//at this line you get an InvalidOperationException
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
do
{
} while (true);
}
void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
do
{
} while (true);
}
}
As an answer to your comment question
#SriramSakthivel Thanks. Is there a way to put tasks in a queue ?
yes you can if you are using .net 4.0 you can use task with ContinueWith and attach it to your UI
taskScheduler it will have the same behavior as if you are using BackgroundWorker
private void TestButton_Click(object sender, EventArgs e)
{
TestButton.Enabled = false;
var uiThreadScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var backgroundTask = new Task(() =>
{
Thread.Sleep(5000);
});
var uiTask = backgroundTask.ContinueWith(t =>
{
TestButton.Enabled = true;
}, uiThreadScheduler);
backgroundTask.Start();
}
I have the following code:
StatusLabel.Content = "Copying files...";
AutoCopy();
StatusLabel.Content = "Finished";
The above code is a button click and when I click the button, I expect to see a label with "Copying files...", then it will copying files via an AutoCopy method and then the label with "Finished"
I do not see "Copying files". All I see is the screen freeze up and then unfreezed with "Finished".
How can I get "Copying files to show..." and only after AutoCopy() is finished, do I want to show "Finished";
As everyone said, your UI (main) thread is blocked during file copy operation.
You need to spin off a worker thread that does everything in the background.
Caution: Multithreading only adds complexity.
{
...
System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
StatusLabel.Content = "Copying files...";
bw.RunWorkerAsync();
...
}
private void DoWork(object sender, DoWorkEventArgs e)
{
AutoCopy();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StatusLabel.Content = "Finished";
}
This case is using C# WPF. I want to instantly disable a button after clicking it to prevent clicking it twice in short succession. I disabled the button in OnClick_Event but still clickable.
Part of source is as below.
private void Button_Click_UpdateBurndownChart(object sender, RoutedEventArgs e)
{
if(threadNotWorking)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
private void startWorkThread()
{
... ...
//after finish required process
updateButton.IsEnabled = true;
}
Is there any way to accomplish this?
you may want to use a dispatcher, there is probably a threading problem (callback function running on seperate thread and trying to access ui which runs on another thread). try this . .
updateButton.Dispatcher.BeginInvoke(
new ThreadStart(() => updateButton.IsEnabled = false),
System.Windows.Threading.DispatcherPriority.Input, null);
instead of
updateButton.IsEnabled = false;
What happens if you were instead to change the order of your events from:
updateButton.IsEnabled = false;
startWorkThread();
To
startWorkThread();
updateButton.IsEnabled = false;
Let me know how this goes.
What it looks like is that you are starting your thread then immediatly enabling your button before your thread has finished. You would be better off using a BackgroundWorker and enable your Button in the RunWorkerCompleted Event. Though you can do something similar by enabling your button using a BeginInvoke at the end of your Process.
public void doWork()
{
System.Threading.Thread.Sleep(10000); //Simulating your Process
Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate() { updateButton.IsEnabled = true; }), System.Windows.Threading.DispatcherPriority.Background);
}
Example with BackgroundWorker
using System.ComponentModel;
public partial class MainWindow : Window
{
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
updateButton.IsEnabled = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000); //Simulating your work
}
private void startWorkThread()
{
bgw.RunWorkerAsync();
}
private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (bgw.IsBusy != true)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
}