I'm developing a WPF application that a backgroundWorker to populate a listBox with file thumbnails and wanted to ask if the approach is ok? The user could interrupt the current background worker at any time.
private readonly List<BackgroundWorker> _backgroundWorkers = new List<BackgroundWorker>();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
var bw = (BackgroundWorker)sender;
// Loop logic
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
bw.ReportProgress(0, fd);
}
}
private void RunWorker()
{
foreach (BackgroundWorker bw in _backgroundWorkers.Where(bw => bw.IsBusy))
{
bw.CancelAsync();
}
// App logic...
int i = _backgroundWorkers.Count() + 1;
var worker = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};
worker.DoWork += bw_DoWork;
worker.RunWorkerCompleted += bw_RunWorkerCompleted;
worker.ProgressChanged += bw_ProgressChanged;
worker.RunWorkerAsync(i);
_backgroundWorkers.Add(worker);
}
The aim is to create a new backgroundWorker and cancel existing busy workers
One backgroundworker should be enough to get all the file thumbnails and update the listbox:
public partial class Form1 : Form
{
private readonly ListBox _listBox;
public Form1()
{
InitializeComponent();
_listBox = new ListBox();
var backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true ,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync( new List<object>() );
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var result = new List<object>();
var backgroundWorker = ( BackgroundWorker ) sender;
var enumerable = ( IEnumerable<object> ) e.Argument;
foreach ( var item in enumerable )
{
if ( backgroundWorker.CancellationPending )
{
e.Cancel = true;
return;
}
// Logic to get file thumbnail
result.Add( item );
backgroundWorker.ReportProgress( 0 , item );
}
e.Result = result;
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_listBox.Items.Add( e.UserState );
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ( e.Cancelled ) return;
if ( e.Error != null )
{
// Handle exception
return;
}
var listOfThumbnails = e.Result;
}
}
Related
background worker in asp.net does not write the content to the web UI when background worker completed. please tell me the reasons why. and how to recover.???
static BackgroundWorker bwProcess;
[WebMethod()]
public static int GetProgress()
{
return Percentage;
}
background worker starts when click event happen
protected void btnClick_Click(object sender, EventArgs e)
{
bwProcess = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
bwProcess.DoWork += new DoWorkEventHandler(bwProcess_DoWork);
bwProcess.ProgressChanged += new ProgressChangedEventHandler(bwProcess_ProgressChanged);
bwProcess.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwProcess_RunWorkerCompleted);
bwProcess.RunWorkerAsync("AsyncWorker");
}
do work event for the backgroundworker
void bwProcess_DoWork(object sender, DoWorkEventArgs e)
{
bwProcess.ReportProgress(1);
for (int i = 0; i <= 100; i++)
{
if (bwProcess.CancellationPending)
{
e.Cancel = true;
return;
}
bwProcess.ReportProgress(i);
Thread.Sleep(20);
}
e.Result = "100 %";
}
this part is not working. it run nut there is no response
void bwProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Response.WriteFile("D:\\Samples.xlsx");
}
You've passed something to e.Result in DoWork, but you don't anything with it in RunWorkerCompleted.
void bwProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var text = e.Result as string;
Response.Write(text);
Response.WriteFile("D:\\Samples.xlsx");
}
In my Windows Form Application, certain directories are copied on user request. Currently copying these directories is running on UI thread as a result, I am not able see any progress of the process.
Currently the copy function is trigger my following code.
private void button5_Click(object sender, EventArgs e)
{
string[] str = comboBox2.Text.Split(' ');
string username = str[2].Replace("[", "").Replace("]", "");
label3.Text = comboBox3.Text;
DialogResult result = MessageBox.Show("Do you want to copy " + comboBox3.Text + " mozilla profile for " + username + " to Selected Servers?", "Confirmation", MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Yes)
{
if (myCheck.Checked == true)
{
string path = getPath(comboBox3.Text);
try
{
string source = path + "\\Appdata";
string dest = "\\\\192.168.1.40\\C$\\Users\\" + username + "\\AppData\\Local\\Mozilla";
Copy(#source, #dest);
}
}
}
}
Copy functions has following Code.
public void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
//Gets size of all files present in source folder.
GetSize(diSource, diTarget);
maxbytes = maxbytes / 1024;
progressBar1.Maximum = maxbytes;
CopyAll(diSource, diTarget);
}
public void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in source.GetFiles())
{
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
total += (int)fi.Length;
copied += (int)fi.Length;
copied /= 1024;
progressBar1.Visible = true;
progressBar1.Step = copied;
progressBar1.PerformStep();
label14.Visible = true;
label14.Text = (total / 1048576).ToString() + "MB of " + (maxbytes / 1024).ToString() + "MB copied";
label14.Refresh();
}
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}
public void GetSize(DirectoryInfo source, DirectoryInfo target)
{
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in source.GetFiles())
{
maxbytes += (int)fi.Length;//Size of File
}
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name);
GetSize(diSourceSubDir, nextTargetSubDir);
}
}
My code is working perfectly but i am not able to see the progress and not able to see the update in Label.
Can somebody help me to run this copy in new thread so that I can see the progress in progress bar.
Here's a simple example to use background worker.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Shown += new EventHandler(Form1_Shown);
// To report progress from the background worker we need to set this property
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
void Form1_Shown(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
}
// On worker thread so do our thing!
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100);
}
}
// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
}
You can do the CopyAll in Do_Work and Report progress to show the progress on UI in a progress bar.
Not going to wrap it up for you but I think the following might get you on the right track.
Given you have "standard" :) button1 and progressBar1 you get nice non-blocking UI update using e.g. Task and BeginInvoke().
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 5;
Task.Factory.StartNew(() =>
{
for (int i = 0; i <= 5; i++)
{
int progress = i;
progressBar1.BeginInvoke((Action)(() =>
{
progressBar1.Value = progress;
}));
Thread.Sleep(250);
}
});
}
Try this pseudo code:
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork +=
new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged +=
new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
private void button5_Click(object sender, EventArgs e)
{
// rest of the code above this...
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// rest of the code above this...
Copy(#source, #dest);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Visible = true;
progressBar1.Step = copied;
progressBar1.PerformStep();
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Completed");
}
I have a handler that returns some string messages. How can i put them into a form application? I mean I would like each time the handler is activated to print the result in a windows form. I now i need to use thread. I don't know how can i programatically create and add changes the name of the forms that pop up when a message is handled. Can someone please tell me how to do it?
I asssume that you have some work you want to do in the background and whenever an amount of progress is don you want to update the form...
Then I would use the backgroundworker:
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10)); //Activating the progressChanged event
}
}
}
Whenever you execute worker.ReportProgress() the event below will fire on the UI thread which is giving you the dynamic updates you wanted
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
When the worker is finished executing the RunWorkerCompleted event will fire
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.tbProgress.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.tbProgress.Text = ("Error: " + e.Error.Message);
}
else
{
this.tbProgress.Text = "Done!";
}
}
Source
This is how I create a thread that does domething reps times:
protected virtual void RedButtonClicked(object sender, System.EventArgs e)
{
Nuker n = new Nuker(target, reps);
bombThread = new Thread(new ThreadStart(n.nuke));
bombThread.Start();
}
The thread class:
public class Nuker
{
private string target;
private int reps;
//...
public void nuke()
{
for(int i=0; i<reps; ++i)
{
ICBM.nuke(target);
Thread.Sleep(5500);
}
System.Console.WriteLine("Done.");
}
}
(I create a new class to store some variables since I can't pass these in ThreadStart().)
Now I would like to have a simple visualisation of the process, let's say printing the current repetition in a text field on a form. How would I use the i from the loop to do that?
In the simplest form you proved a callback in the Nuker class
public Nuker(string target, int reps, Action reportCallback){..}
In the loop you just call reportCallback(i);
Nuker n = new Nuker(target, reps, ReportMethod);
with
private void ReportMethod(int currentIdx)
{
if (InvokeRequired) // Invoke if UI update
...
}
But, probably you want to use the BackgroundWorker that has build in methods for reporting progress on the UI thread. Just check the examples on MSDN.
you can do it with a backgroundworker, it´s one of the easiest threads :-)
below i have post you a sample that i have createt to teach some friends the use of the backgroundworker ;-)
private BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
public void buttonStart_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
bw.RunWorkerAsync(12); //Start
}
public int Pils(int i)
{
Thread.Sleep(2000);
bw.ReportProgress(70, "In the middle of the work..");
Thread.Sleep(2000);
bw.ReportProgress(90, "Returning the result..");
Thread.Sleep(2000);
return (2 * i);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
bw.ReportProgress(20, "Waiting for cancel..");
Thread.Sleep(2000);
if ((bw.CancellationPending == true))
e.Cancel = true;
else
{
bw.ReportProgress(50, "Starting process..");
e.Result = Pils((int)e.Argument);
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bw.ReportProgress(100, "Work done..");
if ((e.Cancelled == true))
textBox1.Text = "Canceled!";
else if (e.Error != null)
textBox1.Text = ("Error: " + e.Error.Message);
else textBox1.Text = e.Result.ToString();
}
private void buttonCancel_Click(object sender, EventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
bw.CancelAsync();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBox1.Items.Add((e.ProgressPercentage.ToString() + "%") + " - " + e.UserState as String);
}
url to the blogpost: link
I have a background worker. Before I invoke the worker I disable a button and make a gif visible. I then invoke the runworkerasync method and it runs fine until comleteion. On the 'RunWorkerCompleted()' I get a cross thread error. Any idea why?
private void buttonRun_Click(object sender, EventArgs e)
{
if (comboBoxFiscalYear.SelectedIndex != -1 && !string.IsNullOrEmpty(textBoxFolderLoc.Text))
{
try
{
u = new UpdateDispositionReports(
Convert.ToInt32(comboBoxFiscalYear.SelectedItem.ToString())
, textBoxFolderLoc.Text
, Properties.Settings.Default.TemplatePath
, Properties.Settings.Default.ConnStr);
this.buttonRun.Enabled = false;
this.pictureBox1.Visible = true;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
//backgroundWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show("Unable to process.\nError:" + ex.Message, Properties.Settings.Default.AppName);
}
}
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
buttonRun.Enabled = true;
pictureBox1.Visible = false;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
u.Execute();
}
It seems to be an issue with VSTO and BackgroundWorker.
The solution is here.
Basically you need to call
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
before you call RunWorkerAsync. Works great.
To avoid instantiating the object every time you may have a static member in you AddIn's main class and reuse it. This way you only instantiate once.
something about VSTO running the background worker on the same thread as the controls. Not sure. I had to check the InvokeRequired
private void buttonRun_Click(object sender, EventArgs e)
{
if (comboBoxFiscalYear.SelectedIndex != -1 && !string.IsNullOrEmpty(textBoxFolderLoc.Text))
{
try
{
u = new UpdateDispositionReports(
Convert.ToInt32(comboBoxFiscalYear.SelectedItem.ToString())
, textBoxFolderLoc.Text
, Properties.Settings.Default.TemplatePath
, Properties.Settings.Default.ConnStr);
this.buttonRun.Enabled = false;
this.pictureBox1.Visible = true;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
//backgroundWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show("Unable to process.\nError:" + ex.Message, Properties.Settings.Default.AppName);
}
}
}
delegate void ReenableRunCallback();
private void ReenableRun()
{
if (this.buttonRun.InvokeRequired)
{
ReenableRunCallback r = new ReenableRunCallback(ReenableRun);
this.buttonRun.Invoke(r, null);
}
else
this.buttonRun.Enabled = true;
}
private void HideProgress()
{
if (this.pictureBox1.InvokeRequired)
{
ReenableRunCallback r = new ReenableRunCallback(HideProgress);
this.pictureBox1.Invoke(r, null);
}
else
this.pictureBox1.Visible = false;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ReenableRun();
HideProgress();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
u.Execute();
}