Here i'm using multiple backgroundworkers to do one network related operation.Basically its a process of checking the user is registered with my page or not. I'm having nearly 1000 accounts to check, hence i'm using datagridview to import users and fetch the username from same for checking purpose. My code working fine and displays the result, But the problem is when updating the status in datagridview its not that much effective. In that long process method i used to set status text for every process of method, like login process started logged in failed to login .But Backgroundworker does not updating the status column. It displaying the status after all backgroundworker completes only. Can anyone give me an idea for how to update the status for each account ??
thanks in advance
It my code:
int threadNum;
public BackgroundWorker bcheker;
private void toolStripButton2_Click(object sender, EventArgs e)
{
if (wbcheckStatus == WorkerStatus.NotStarted || wblogcheckStatus == WorkerStatus.Completed)
{
threadNum = -1;
SetControlsStatus(ProgramStatus.BChecking);
toolStripButton2.Image = aTumblr.Properties.Resources.control_stop;
for (int i = 0; i < 4; i++)
{
bcheker = new BackgroundWorker();
bcheker.DoWork += new DoWorkEventHandler(bcheker_dowork);
bcheker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bchecker_completed);
bcheker.ProgressChanged +=bcheker_ProgressChanged;
bcheker.WorkerReportsProgress = true;
bcheker.WorkerSupportsCancellation = true;
bcheker.RunWorkerAsync();
}
}
else
{
threadNum = 10000;
bcheker.CancelAsync();
SetControlsStatus(ProgramStatus.BlogChecking);
}
}
public void bcheker_dowork(object sender, DoWorkEventArgs e)
{
while (!bcheker.CancellationPending)
{
int rownum = Interlocked.Increment(ref threadNum);
if (rownum >= bchecktableModel.Rows.Count)
{
break;
}
Thread.Sleep(1000);
BlogChecker bc = new BlogChecker(bchecktableModel[rownum, 1].Text, bchecktableModel[rownum, 2]);
bc.check();
wblogcheckStatus = WorkerStatus.Running;
}
if (bcheker.CancellationPending)
{
wblogcheckStatus = WorkerStatus.Completed;
SetControlsStatus(ProgramStatus.BCheckingDone);
}
}
public void bcheker_ProgressChanged(Object sender, ProgressChangedEventArgs e)
{
}
public void bchecker_completed(object sender, EventArgs e)
{
if (threadNum == bchecktableModel.Rows.Count+1)
{
SetControlsStatus(ProgramStatus.BCheckingDone);
wblogcheckStatus = WorkerStatus.Completed;
}
}
First of all, It looks like you have an issue of multiple handlers registering to the DoWorkEvent.
I am not sure what is happening, but I would check How many time the bcheker_dowork method is being called.
In order to report about your progress, use the BackgroundWorker.ReportProgress method.
public void bcheker_ProgressChanged(Object sender, ProgressChangedEventArgs e)
{
//Update your progress here
}
Also, in your DoWork() method, you shouldn't be using "bcheker" as that will simply refer to the last BackgroundWorker created. Instead, cast the sender parameter to a local BackgroundWorker variable and use that.
Related
Im working with WinForms.
I want to populate ListView from background thread but when im Invoking listview my program stops and shows an error. The error is "Cannot acces a disposed object. Object name is: ListView." And when i put this method
lvValidate.Invoke((Action)delegate
{
lvValidate.Items.Add(listitem);
});
in a try-catch block my program starts lagging. I dont know where is the problem,but my Invoke method is:
static class Intercept
{
internal static void Invoke(this Control control, Action action)
{
control.Invoke(action);
}
}
The error only showing when i close the form and open another form (in the same program). In the Form which contains the ListView the data is unreadable and it seems loads a thousands times.
Here's what my DoWork,ProgressChanged,RunWorkerCompleted event does.
private void bgwLoad_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string commandText = "SELECT * FROM works";
MySqlCommand command = new MySqlCommand(commandText, connection);
MySqlDataAdapter da = new MySqlDataAdapter(command);
connection.Close();
connection.Open();
reader = command.ExecuteReader();
connection.Close();
DataTable dt = new DataTable();
da.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
{
DataRow dr = dt.Rows[i];
ListViewItem listitem = new ListViewItem(dr["ID"].ToString(), dr["Date"].ToString());
listitem.SubItems.Add(dr["Date"].ToString());
listitem.SubItems.Add(dr["Name"].ToString());
listitem.SubItems.Add(dr["WorkNumber"].ToString());
listitem.SubItems.Add(dr["WorkCode"].ToString());
listitem.SubItems.Add(dr["CoreThread"].ToString());
listitem.SubItems.Add(dr["Tech"].ToString());
listitem.SubItems.Add(dr["From"].ToString());
listitem.SubItems.Add(dr["To"].ToString());
listitem.SubItems.Add(dr["Validate"].ToString());
listitem.SubItems.Add(dr["Validate2"].ToString());
lvValidate.Items.Add(listitem);
}
}
private void bgwLoad_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
}
private void bgwLoad_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
picLoading.Visible = false;
}
Try the following:
Dispatcher.CurrentDispatcher.Invoke(() => {
lvValidate.Items.Add(listitem);
});
EDIT:
Or Try this:
public static void AddItem(ListItem listitem)
{
if (lvValidate.InvokeRequired)
{
AddItemDelegate d = new AddItemDelegate (AddItem);
lvValidate.Invoke(d, new object[] { listitem });
}
else
{
lvValidate.Invoke(new Action(() =>
{
lvValidate.Items.Add(listitem);
}));
}
}
delegate void AddItemDelegate(ListItem listitem);
Then call:
AddItem(listitem);
According to your comments, you are trying to retrieve Data from a DB using a DataAdapter, then handing out the Data piecemeal. This will not work for several reasons:
DataAdapter
The DataAdapter Classes all have in common that they only work, while the DBConnection is actively open. That is why you get "Cannot acces a disposed object". Because by the time you try to use it, it the connection is already Disposed. And disposing is not a thing you should ever delay. Or split up at all. Keep the using (that you hopefully got) right where it is, inside the DoWork().
For that reason you always have to copy the data of the DataAdapter into a non-Adapter collection. Really any old list would do. This will temporarily double the Memory load and might procude some stuff for the GC to clean up, but is really they only adviseable way.
Bulk Writes only in Completed
While you can theoretically hand out Partial Reads/process results via Progress Reporting, this is not adviseable to try. Writing a GUI has a massive overhead. The first and only time I did that, I ended up locking up my GUI thread with Write and Draw Operations. It looked like I had never done Multitasking. Updating a Progress bar is just about low cost enough to never really cause issues.
The default pattern is to only over write relevant amount of data after you got it all.
If you run into a exception or cancel, the pattern is to asume that all data is faulty and not have it on the UI.
I like to call the BackgroundWorker "Multitasking/-Threading Training Wheels". They teach you all those things. The firt part, by making the handing out akward. The second part, by actually throwing a Exception if you try to use the Result in invalid cases.
You propably retreive too much
Perhaps the most common mistake with DB's, is trying to retrieve a lot of data to then do processing or filtering in the Client. A common mistake, so avoid it.
There is a limit to how much data a User can process whatever you would define as 1 page. Never more then 100 Data Fields at once is my advice. If you got do Filtering, Pagination or the sort, always do it in the Query. Moving this stuff to the client moves a lot of unesessary data over the Network to then be slower at processing/Filtering then the DB would ever have been.
Example code
This actually is my first BGW Project. I updated it a bit over the years, but the bulk of it is still valid:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
I read both BackgroundWorker still freezes UI and UI still freezes using backgroundWorker but neither have helped me solve my issue. I asked a question a few days ago about Datatable not displaying information after SQL query fill in backgroundworker, which I eventually solved by using Invoke and a delegate.
My current code (shown below) gives me the results I want, except for two things: it doesn't actually display the progress bar (at least that I can see) and it freezes the UI while running:
DataTable tab1table = new DataTable();
public Form1()
{
InitializeComponent();
Instantiatesearch1Thread();
}
private void Instantiatesearch1Thread()
{
search1Thread.WorkerReportsProgress = true;
search1Thread.WorkerSupportsCancellation = true;
search1Thread.ProgressChanged += search1Thread_ProgressChanged;
search1Thread.DoWork += search1Thread_Dowrk;
search1Thread.RunWorkerCompleted += search1Thread_RunWorkerCompleted;
}
private void sbutton1_Click(object sender, EventArgs e)
{
search1Thread.RunWorkerAsync();
}
void search1Thread_Dowrk(object sender, DoWorkEventArgs e)
{
int percentprogress = 0;
var worker = sender as BackgroundWorker;
// Ensure process is thread-safe by using Invoke method
Invoke(new MethodInvoker(delegate
{
// Search1 button event handler
using (SqlConnection conn = new SqlConnection(Connection_Info))
{
conn.Open();
using (SqlDataAdapter cmd = new SqlDataAdapter(comboBox1SQL, conn))
{
//MessageBox.Show("Working");
if (comboBox1.Text.Contains("ID"))
{
long para = long.Parse(search1.Text);
cmd.SelectCommand.Parameters.Add(new SqlParameter
{
ParameterName = "#combo1Par",
Value = para,
SqlDbType = SqlDbType.BigInt
});
}
// Clear datatable if it contains any information and then fill it
if (tab1table != null)
tab1table.Clear();
cmd.Fill(tab1table);
// Set Progress Bar info
search1Progress.Maximum = tab1table.Rows.Count;
groupBox2.Controls.Add(search1Progress);
search1Progress.BringToFront();
}
}
// This is a long calculation where I am reporting the
// progress every iteration (which is probably not helping,
// but didn't change anything when commented out).
if(!String.IsNullOrEmpty(search1.Text) && tab1Combo1.SelectedIndex > -1 || tab1Combo2.SelectedIndex > -1)
{
for (int i = tab1table.Rows.Count - 1; i >= 0; i--)
{
DataRow dr = tab1table.Rows[i];
if (!dr.ItemArray.Contains(search1.Text.ToString()))
{
dr.Delete();
tab1table.AcceptChanges();
}
percentprogress++;
worker.ReportProgress(percentprogress);
}
}
}));
}
void search1Thread_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
search1Progress.Value = e.ProgressPercentage;
}
void search1Thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
tab1datatable.DataSource = null;
tab1datatable.DataSource = tab1table;
tab1datatable.Update();
groupBox2.Controls.Remove(search1Progress);
MessageBox.Show(" Your search returned " + tab1table.Rows.Count.ToString() + " results");
}
Can anyone point out what I am doing wrong here?
My issue may have something to do with Invoke as I know it's not the best method with BackgroundWorker, but I couldn't get it to update the UI using any other method.
I recently attempted to thread a foreach loop because it was freezing the UI when it ran. This loop looks through a bunch of log files and retrieves information from them line by line, then adds it to a listView(also prints something to a richtextBox). The issue I'm having is that after threading this loop, it seems that neither the listView nor the richTextBox update anymore. I know for a fact that the method is being called, because when I put a MessageBox after that 'if statement', it opens just fine but everything else refuses to work.
public void searchForAll(object sender, DoWorkEventArgs e)
{
if (//argument)
{
listViewEx1.Items.Clear();
int logcount = 0;
richTextBoxEx1.Text += "Print something";
richTextBoxEx1.Text += "\n";
richTextBoxEx1.SelectionStart = richTextBoxEx1.Text.Length;
richTextBoxEx1.ScrollToCaret();
foreach (User user in s.Friends)
{
foreach (string log in Directory.GetFiles(path, "*.log"))
{
string[] fileToRead = File.ReadAllLines(log);
foreach (string line in fileToRead)
{
if (line.Contains(user.Handle) && line.Contains("-r"))
{
if (!isDuplicate(c))
{
listViewEx1.Items.Add(new ListViewItem(user.Handle)
{
SubItems = { c }
});
dupCheck.Add(c);
logcount++;
}
}
}
}
dupCheck.Clear();
Thread.Sleep(1000);
}
richTextBoxEx1.Text += "Print something";
}
}
}
It isn't recommended to directly change parts of the UI from within a BackgroundWorker.DoWork event, instead I recommend using BackgroundWorker.ReportProgress and editing the UI inside THAT method.
Here is an example of how I would implement changing a textbox's value to 'x':
public void searchForAll(object sender, DoWorkEventArgs e)
{
//Define background worker
var MyBack = (BackgroundWorker)sender;
for(...)
{
//Send some data to ReportProgress
MyBack.ReportProgress(0, "any object of any form goes here");
}
}
Then in the report progress method:
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
textBox1.Text = e.UserState.ToString();
}
Please note that you have to set Backgroundworker1.WorkerReportsProgress = true; otherwise the worker won't be able to report the progress.
So I can't say for sure this is the issue but I'm just about positive it is. I have a recordset of IVR calls to make. I put the data for each one in a concurrent queue and start 5 background workers to start working from the queue. However, after making 2 calls, the calls stop coming until one person hangs up, then it moves on to call number 3,4,5 etc. Are the any issues with this code?
It seems like the background workers are blocking eachother from calling the same method...? Is that possible?
private ConcurrentQueue<DataTable> _ivrCallsQueue = new ConcurrentQueue<DataTable>();
private List<BackgroundWorker> _ivrCallers = new List<BackgroundWorker>();
public overrid void Process()
{
foreach(DataRow row in _tblRecordsToProcess.Rows)
{
_workingActionItem = actionItemDAL.GetActionItemFromId(Convert.ToInt32(row["FNActionItemId"].ToString()));
var workingActionItemsTable = actionItemDAL.GetActionItemParamValues(Convert.ToInt32(row["FNActionItemId"].ToString()));
ivrCallsQueue.Enqueue(workingActionItemsTable);
}
StartCalls();
while (_ivrCallers.Count != 0)
{
testurls = testurls;
}
}
private void StartCalls()
{
int maxLines = 5;
if (_ivrCallsQueue.Count < maxLines)
{
maxLines = _ivrCallsQueue.Count;
}
for (int i = 0; i < maxLines; i++)
{
DataTable workingCall = new DataTable();
_ivrCallsQueue.TryDequeue(out workingCall);
BackgroundWorker ivrCaller = new BackgroundWorker();
_ivrCallers.Add(ivrCaller);
ivrCaller.DoWork += delegate(object sender, DoWorkEventArgs e)
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
_ivrCallers.Remove(ivrCaller);
};
ivrCaller.RunWorkerCompleted += (bw_AnalyzeResults);
ivrCaller.RunWorkerAsync();
}
}
private void bw_AnalyzeResults(object sender, RunWorkerCompletedEventArgs e)
{
DataTable workingCall = new DataTable();
if (_ivrCallsQueue.Count != 0)
{
_ivrCallsQueue.TryDequeue(out workingCall);
BackgroundWorker ivrCaller = new BackgroundWorker();
ivrCaller.DoWork += delegate(object completeSender, DoWorkEventArgs completeArgs)
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
_ivrCallers.Remove(ivrCaller);
};
ivrCaller.RunWorkerCompleted += (bw_AnalyzeResults);
ivrCaller.RunWorkerAsync();
}
else
{
}
}
private void RequestIVR(DataTable workingTable,int fnActionItemID)
{
var urlRequest = "http://uccx_http_trigger:9080/test?strTestMode=1&strTaskID=" + fnActionItemID;
var webClient = new WebClient { UseDefaultCredentials = true, Proxy = WebRequest.DefaultWebProxy };
DecodeResponseType(GetValueFromElement("Response Code was ", webClient.DownloadString(urlRequest)));
}
This will spawn at most five threads that each attempt to pull the next item from the queue and process it. If the queue is empty the attempt will fail and the thread will simply exit:
private List<System.Threading.Thread> Threads = new List<System.Threading.Thread>();
private ConcurrentQueue<DataTable> _ivrCallsQueue = new ConcurrentQueue<DataTable>();
private void StartCalls()
{
int maxLines = Math.Min(5 , _ivrCallsQueue.Count);
for (int i = 0; i < maxLines; i++ )
{
System.Threading.Thread T = new System.Threading.Thread(delegate()
{
DataTable workingCall;
while (_ivrCallsQueue.TryDequeue(out workingCall))
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
}
});
Threads.Add(T);
T.Start();
}
}
The threads will keep running until all the items have been processed.
It looks like bw_AnalyzeResults does pretty much the same thing that StartCalls() does. In other words, when the background worker has finished its work, you immediately enqueue the same work to happen again, recursively forever?
By the looks of it, you want bw_AnalyzeResults to analyze the results returned by calling your web service. That is not what is happening at the moment.
The code below taken from the bw_AnalyzeResults event handler is scheduling a background job and making itself handle the RunWorkerCompleted event. So, presumably the software keeps going around and around executing bw_AnalyzeResults forever until you kill the process?
private void bw_AnalyzeResults(object sender, RunWorkerCompletedEventArgs e)
{
ivrCaller.DoWork += delegate(object completeSender, DoWorkEventArgs completeArgs)
{
RequestIVR(workingCall, Convert.ToInt32(workingCall.Rows[2][0].ToString()));
_ivrCallers.Remove(ivrCaller);
};
ivrCaller.RunWorkerCompleted += (bw_AnalyzeResults);
}
I'm stuck trying to update a progressbar from other threads ran in a different class. To explain what I do I think a picture will be better. I want to update the progressbar in the //HERE point :
I've tried using a delegate, tried with ReportProgress and I think i've basically tried to use everything google reported in the first 100 results, without success. I'm still learning WPF and this might be silly way to proceed, i'm looking for a quick and dirty way to get the work done but feel free to tell me what I should redesign for a cleaner application.
EDIT : More code.
In ExecutorWindow.xaml.cs :
public void RunExecutor()
{
// CREATE BACKGROUNDWORKER FOR EXECUTOR
execBackground.DoWork += new DoWorkEventHandler(execBackground_DoWork);
execBackground.RunWorkerCompleted += new RunWorkerCompletedEventHandler(execBackground_RunWorkerCompleted);
execBackground.ProgressChanged += new ProgressChangedEventHandler(execBackground_ProgressChanged);
execBackground.WorkerReportsProgress = true;
execBackground.WorkerSupportsCancellation = true;
// RUN BACKGROUNDWORKER
execBackground.RunWorkerAsync();
}
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run();
}
private void execBackground_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("RunWorkerCompleted execBackground");
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
// TESTING
private void updateProgressBar(int i)
{
ExecutorProgressBar.Value += i;
}
public delegate void callback_updateProgressBar(int i);
In Executor.cs :
public void Run()
{
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
// HERE ?
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
while (CurrentRunningThreads > 0) { }
logfile.Close();
MessageBox.Show("All Tasks finished");
}
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
string[] options = (string[])e.Argument;
string machine = options[0];
string script = options[1];
// UPDATE HERE PROGRESSBAR ?
RemoteProcess myRemoteProcess = new RemoteProcess(machine, script);
string output = myRemoteProcess.TrueExec();
// UPDATE HERE PROGRESSBAR ?
this.logfile.WriteLine(output);
}
private void backgroundWorkerRemoteProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
CurrentRunningThreads--;
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//myExecWindow.ExecutorProgressBar.Value = e.ProgressPercentage; // TESTING
//ExecutorWindow.callback_updateProgressBar(1); // TESTING
}
EDIT 2 : I got it! Simple in fact, but i guess I've been looking too close to find out.
In my ExecutorWindow class :
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run(sender);
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
And in my Executor class :
private BackgroundWorker myExecutorWindow;
[...]
public void Run(object sender)
{
myExecutorWindow = sender as BackgroundWorker;
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
[...]
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
myBackgroundWorker.ReportProgress(1);
// PROCESSING MY STUFF HERE
myBackgroundWorker.ReportProgress(1);
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myExecutorWindow.ReportProgress(1);
}
Thank you !
You can run any method on the UI thread with this very basic sample
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
{
this.progressBar.Value= 20; // Do all the ui thread updates here
}));
Running commands inside the Dispatcher.Invoke(...), you can actually interact with the UI from any worker thread, where otherwise you would get an exception.
If you really need to have the ultimate control on the background threads & main (UI) thread updates, here is a fantastic tutorial on that: http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html
You should be able to use the Dispatcher.Invoke method
e.g.
Dispatcher.Invoke(
new System.Action(() => myProgressBar.Value = newValue)
);
I got it! Simple in fact, but i guess I've been looking too close to find out.
In my ExecutorWindow class :
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run(sender);
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
And in my Executor class :
private BackgroundWorker myExecutorWindow;
[...]
public void Run(object sender)
{
myExecutorWindow = sender as BackgroundWorker;
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
[...]
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
myBackgroundWorker.ReportProgress(1);
// PROCESSING MY STUFF HERE
myBackgroundWorker.ReportProgress(1);
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myExecutorWindow.ReportProgress(1);
}
I found a really simple solution to create a thread to run any block of code as well as handle Invocation back to the main thread to change the control's properties. It works out of the box with .NET 4.5 and the lambda call on the Dispatcher could be adapted to work with earlier versions of .NET. The main benefit is it's just so blissfully simple and perfect when you just need a quick thread for some really basic bit of code.
So presuming you have a progress bar somewhere on your dialog in scope do this:
progBar.Minimum = 0;
progBar.Maximum = theMaxValue;
progBar.Value = 0;
Dispatcher disp = Dispatcher.CurrentDispatcher;
new Thread(() => {
// Code executing in other thread
while (progBar.Value < theMaxValue)
{
// Your application logic here
// Invoke Main Thread UI updates
disp.Invoke(
() =>
{
progBar.Value++;
}
);
}
}).Start();
You also need to ensure you have a reference to WindowsBase.dll
If you want a more reusable snippet of code running as the thread start you could use a method as the delegate but I find the inline lambda so easy for simple tasks and you don't need to deal with events as with the Background Worker approaches.