this question and Answer ->
C# add child to Parents treeView
is Stuck Because Volume of data is enormous. How to backgroundWorker or Thread Cell ProgressBar
Help me implement it with backgroundWorker Thread Progress Bar
code
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
//// Or by
Thread th1 = new Thread(new ThreadStart(fun1));
th1.Start();
}
private void fun1()
{
/// Business_tb_Language IS ClassLibrary (DLL) return DataTable
Business_tb_Language businessTbLanguage = new Business_tb_Language();
DataTable dt1 = businessTbLanguage.bRead();
LoadTreeView(dt1);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
/// Business_tb_Language IS ClassLibrary (DLL) return DataTable
Business_tb_Language businessTbLanguage = new Business_tb_Language();
DataTable dt1 = businessTbLanguage.bRead();
LoadTreeView(dt1);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("error");
}else if (e.Cancelled)
{
MessageBox.Show("cancel");
progressBar1.Value = 0;
}
else
{
MessageBox.Show("ok");
progressBar1.Value = 0;
}
}
private void LoadTreeView(DataTable dt)
{
var dNodes = new Dictionary<string, TreeNode>();
foreach (DataRow dRow in dt.Rows)
{
string sSublang = dRow["Sub_lang"].ToString();
string sCode = dRow["code"].ToString();
string sName = dRow["name"].ToString();
if (sSublang == "0")
{
var tn = treeView1.Nodes.Add(sCode, sName);
dNodes.Add(sCode, tn);
}
else
{
string[] arrSubLang = sSublang.Split('_');
for (int i = arrSubLang.Length - 1; i >= 0; i--)
{
string sFindCode = arrSubLang[i];
var tnLastParent = default(TreeNode);
if (dNodes.ContainsKey(sFindCode))
{
var tn = dNodes[sFindCode];
if (tnLastParent != default(TreeNode))
{
tn.Nodes.Add(tnLastParent);
tnLastParent = tn;
}
else if (!dNodes.ContainsKey(sCode))
{
tnLastParent = tn.Nodes.Add(sCode, sName);
dNodes.Add(sCode, tnLastParent);
}
}
}
}
}
treeView1.ExpandAll();
}
my code
Error in function LoadTreeView(DataTable dt)
Line
var tn = treeView1.Nodes.Add(sCode, sName);
** Text Error **
An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Action being performed on this control is being called from the wrong thread. Marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action.
Read Business_tb_Language ( IS ClassLibrary (DLL) return DataTable ) AND Show in treeView BY backgroundWorker or Thread Cell ProgressBar
Explain more :
This function "LoadTreeView (DataTable dt)" and "Business_tb_Language" Correct and true
But I want to implement the BY backgroundWorker or Thread Cell ProgressBar
Thank you.
Related
I get this error if I click a button that starts the backgroundworker twice.
This BackgroundWorker is currently busy and cannot run multiple tasks concurrently
How can i make it work when it update the records ?
this is my code
using (SqlConnection con = new SqlConnection("****"))
{
con.Open();
using (SqlCommand com = new SqlCommand("UPDATE indebtedness SET collected=#collected,Payment_Date=#Payment_Date,Payment_Date2=#Payment_Date2 WHERE Subscriber_No=#Subscriber_No and company_name=#company_name and indebtedness_name=#indebtedness_name ", con))
{
com.Parameters.AddWithValue("#company_name", company_name.Text);
com.Parameters.AddWithValue("#indebtedness_name", indebtedness_name.Text);
com.Parameters.AddWithValue("#Payment_Date2", DateTime.Today.ToString());
com.Parameters.Add("#Payment_Date", SqlDbType.Date);
com.Parameters.Add("#Subscriber_No", SqlDbType.BigInt);
com.Parameters.Add(new SqlParameter("#collected", SqlDbType.Decimal) { Precision = 18, Scale = 3 });
int countSuccess = 0;
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
com.Parameters["#Subscriber_No"].Value = Convert.ToInt64(dataGridView1.Rows[i].Cells[0].Value);
com.Parameters["#collected"].Value = Convert.ToDecimal(dataGridView1.Rows[i].Cells[1].Value);
com.Parameters["#Payment_Date"].Value = (dataGridView1.Rows[i].Cells[2].Value); //hope this is a date, not a string. If it's a string, parse it instead
int numUpd = com.ExecuteNonQuery();
countSuccess += numUpd;
}
backgroundWorker1.RunWorkerAsync();
progressBar1.Show();
MessageBox.Show($"Successfully UPDATED {countSuccess} of {dataGridView1.Rows.Count} rows");
}
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for(int i=1; i<=dataGridView1.Rows.Count; i++)
{
Thread.Sleep(10);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ReportProgress(i);
}
}
private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label6.Text = e.ProgressPercentage.ToString() + "%";
if(label6.Text=="100%")
{
label7.Text = "Record UPdated Successfully .. ";
}
}
i try to update the records and show the result is the bar when it working
Code would look like this :
namespace ConsoleApplication1
{
public class State
{
public List<string> data { get; set; }
}
public class MyBackgroundWorker
{
public static BackgroundWorker backgroundWorker1 = new BackgroundWorker();
public void Test()
{
State state = new State();
state.data = new List<string> { "Some Data" };
int progress = 50;
backgroundWorker1.ReportProgress(progress, state);
}
}
class Program
{
static void Main(string[] args)
{
}
private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
State state = e.UserState as State;
//add code to write to datatable here
}
}
}
I'm starting to work with BackgroundWorker to try it out, and I'm wondering how can I stop a query which returns an Object List and that may take some time to execute.
I guess that I can't stop the query on the server, but what I'm specifically looking for is to stop the read of the SqlDataReader which contains the result of this query.
Here's a code sample with my BackgroudnWorker and a sample query :
public partial class Form1 : Form
{
private BackgroundWorker worker;
public Form1 (Point location)
{
this.Location = location;
InitializeComponent();
worker = new BackgroundWorker
{
WorkerSupportsCancellation = true,
};
worker.DoWork += this.Worker_DoWork;
worker.RunWorkerCompleted += this.Worker_RunWorkerCompleted;
}
#region Form
private void ButtonBack_Click (object sender, EventArgs e)
{
if (worker.IsBusy && !worker.CancellationPending)
{
worker.CancelAsync();
}
this.Close();
}
private void TextBoxSearch_TextChanged (object sender, EventArgs e)
{
while (worker.IsBusy)
{
if (worker.IsBusy && !worker.CancellationPending)
{
worker.CancelAsync();
}
}
worker.RunWorkerAsync();
}
#endregion
#region Worker
private void Worker_RunWorkerCompleted (object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
if (worker.IsBusy && !worker.CancellationPending)
{
worker.CancelAsync();
}
Console.WriteLine(e.Error.Message);
}
}
private void Worker_DoWork (object sender, DoWorkEventArgs e)
{
if (!worker.CancellationPending)
{
// Where I'd like to cut the IEnumerable if worker.CancellationPending to shorten time of process
foreach (LigneHS ligne in GetLignesHS(worker.CancellationPending))
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
// Do work
}
}
else
{
e.Cancel = true;
return;
}
}
#endregion
// Sample query
internal static IEnumerable<LigneHS> GetLignesHS (bool cancellationPending)
{
string requete = "SELECT * FROM [MY_TABLE] ORDER BY [date] DESC";
SqlConnection conn = BDD.OpenBDD16();
SqlCommand command = new SqlCommand(requete, conn);
List<LigneHS> lignes = new List<LigneHS>();
LigneHS ligne = new LigneHS();
try
{
if (!cancellationPending)
{
SqlDataReader reader = command.ExecuteReader();
while (reader.Read() && !cancellationPending)
{
ligne = new LigneHS();
if (reader["id"] != DBNull.Value)
{
ligne.Id = Convert.ToInt32(reader["id"]);
// filtering null values for every column
lignes.add(ligne);
// tried to add the yield return here, but can't inside a try-catch
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("GetLignesHS : " + ex.Message);
}
finally
{
command.Dispose();
conn.Close();
}
return lignes;
}
}
As it is here, cancellationPending of the query doesn't update if the worker is asked to stop.
I first thought of using a yield return to interrupt the foreach if the BackgroundWorker is asked to stop. But, I'm using a try-catch in the treatment of the query, so I can't.
I know that using a CancellationToken as I did when I used straight thread will work, but I wonder if it is the best way to achieve what I want here.
Still struggling to get live data from my database to datagridview. This time I am attempting to do it via background worker. The only thing it gives from doWork method is an INT for backgroundWorker1_ProgressChanged method. I want to send my objects such as DataTable to it as well, so I could refresh data which is on main thread Form1. How can I pass my objects to backgroundWorker1_ProgressChanged event? Marked the place in the code where I need the data from DoWork
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string IP_db_mysql = "1";
string Port_db__mysql = "1";
string UserName_db__mysql = "1";
string Password_db__mysql = "1";
string dbName__mysql = "1";
string connString = "Server =" + IP_db_mysql + "; Port =" + Port_db__mysql + "; Database =" + dbName__mysql + "; Uid =" + UserName_db__mysql + ";Pwd =" + Password_db__mysql + ";";
MySqlConnection conn = new MySqlConnection();
DataTable dt = new DataTable();
string query = "SELECT * FROM mysql.test_table;";
int sum = 0;
for (int i = 1; i <= 10; i++)
{
sum = sum + i;
try
{
// open connectipon
conn.ConnectionString = connString;
conn.Open();
// build command
MySqlCommand cmd = new MySqlCommand(query, conn);
// execute command
MySqlDataReader dataReader = cmd.ExecuteReader();
// fill datatable
dt.Load(dataReader);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
// Calling ReportProgress() method raises ProgressChanged event
// To this method pass the percentage of processing that is complete
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
// Check if the cancellation is requested
if (backgroundWorker1.CancellationPending)
{
// Set Cancel property of DoWorkEventArgs object to true
e.Cancel = true;
// Reset progress percentage to ZERO and return
backgroundWorker1.ReportProgress(0);
return;
}
}
// Store the result in Result property of DoWorkEventArgs object
e.Result = sum;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
labelProgress.Text = e.ProgressPercentage.ToString() + "%";
**----------->> dataGridView1.DataSource = HOW TO GET DATATABLE FROM DO WORK METHOD !? <<------------------------**
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
labelProgress.Text = "Processing cancelled";
}
else if (e.Error != null)
{
labelProgress.Text = e.Error.Message;
}
else
{
labelProgress.Text = e.Result.ToString();
}
}
private void button23_Click(object sender, EventArgs e)
{
// Check if the backgroundWorker is already busy running the asynchronous operation
if (!backgroundWorker1.IsBusy)
{
// This method will start the execution asynchronously in the background
backgroundWorker1.RunWorkerAsync();
}
}
private void button24_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
// Cancel the asynchronous operation if still in progress
backgroundWorker1.CancelAsync();
}
}
At the end of your BackgroundWorker's DoWork you need to set its DataSource to your result. However, since you're in a background worker if you do that directly you'll get an exception because WinForms won't let you access the UI from another thread.
You can do this with the following code:
// create a method to handle updating the datasource
public void UpdateDataGridViewSource(object data)
{
// check if we need to swap thread context
if(this.dataGridView1.InvokeRequired)
{
// we aren't on the UI thread. Ask the UI thread to do stuff.
this.dataGridView1.Invoke(new Action(() => UpdateDataGridViewSource(data)));
}
else
{
// we are on the UI thread. We are free to touch things.
this.dataGridView1.DataSource = data;
this.dataGridView1.DataBind();
}
}
// at the end of your DoWork()
this.UpdateDataGridViewSource(result);
Use a state object like code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Data;
namespace ConsoleApplication76
{
class Program
{
static void Main(string[] args)
{
}
}
public enum ReportTypes
{
PROGRESS,
DATATABLE
}
public class Report
{
public int progress { get; set; }
public ReportTypes type { get; set; }
public DataTable table { get; set; }
}
public class Worker
{
public BackgroundWorker backgroundWorker1 { get; set; }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Report report = new Report();
report.progress = 50;
report.type = ReportTypes.PROGRESS;
backgroundWorker1.ReportProgress(50, report);
DataTable dt = new DataTable();
report.table = dt;
report.type = ReportTypes.DATATABLE;
backgroundWorker1.ReportProgress(50, report);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int progress = e.ProgressPercentage;
Report report = e.UserState as Report;
switch (report.type)
{
case ReportTypes.PROGRESS :
BeginInvoke((MethodInvoker)delegate
{
WriteStatusAndError("Query Completed");
});
break;
case ReportTypes.DATATABLE :
break;
}
}
}
}
I have a method in my class that has some loops inside.
Main purpose of this method is converting some files so I put a progressbar in my form that should get updated after each file has been converted.
I tried every possible combination and I read everything I could but I couldn't solve this issue.
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
converterProgressBar.Value = e.ProgressPercentage;
}
is called only after the main loop of my method has been executed.
This is my method:
public string Convert()
{
convertBtn.Enabled = false;
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
totalCount = files.length;
bw.RunWorkerAsync();
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
countFile++;
if (chk.Checked)
{
class1.DoJob();
}
using (// some code))
{
using (//some other code))
{
try
{
using (// again some code)
{
// job executing
}
}
catch (exception
{
}
}
}
convertedVideosL.Text = txtToUpdate;
convertedVideosL.Refresh();
}
countFile = countFile + 1;
MessageBox.Show("Done");
countFile = -1;
return outputFile;
}
And here are the BackgroundWorker Event Handlers:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= totalCount; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
}
else
{
int progress = Convert.ToInt32(i * 100 / totalCount);
(sender as BackgroundWorker).ReportProgress(progress, i);
}
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
converterProgressBar.Value = e.ProgressPercentage;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == false)
{
convertedVideosL.Text = "Finished!";
}
else
{
convertedVideosL.Text = "Operation has been cancelled!";
}
}
But I cannot get to update the progress bar for every file that is converted.
It waits for the foreach loop to end and then calls bw_ProgressChanged.
If I put RunWorkerAsync() inside the foreach loop an exception is thrown that says the BackgroundWorker is busy and cannot execute other tasks.
It seems to me obvious that DoWork() only executes a for loop then it shouldn't be aware of the conversion going on but ProgressChanged should be fired by ReportProgress(progress,i).
Could please someone explain me why and help me with a solution?
Thanks!
Currently the conversion is not executed by the instance of the BackgroundWorker type. The conversion should be called from the DoWork event handler.
Please consider extracting the conversion-related functionality:
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
// Details...
}
into the separate method. After that just call the method from the DoWork event handler.
Pseudo-code to demonstrate the idea:
public void StartConversion()
{
...
TWorkerArgument workerArgument = ...;
worker.RunWorkerAsync(workerArgument);
// No message box here because of asynchronous execution (please see below).
}
private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = Convert(worker, (TWorkerArgument)e.Argument);
}
private static TWorkerResult Convert(BackgroundWorker worker, TWorkerArgument workerArgument)
{
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
// Details...
worker.ReportProgress(percentComplete);
}
return ...;
}
private void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Show the message box here if required.
}
Please replace the TWorkerArgument and TWorkerResult types appropriately.
Also, please refer to the example which uses the BackgroundWorker class for the additional details: How to: Implement a Form That Uses a Background Operation, MSDN.
I have been trying to implement a BackgroundWorker into my application, and so far, it has not gone well. On a new thread, I want to open up a new Form that will have a progressbar and a label to report progress, however, this is not working well. When I call ShowDialog, the application does not respond any more. Is this because my code is running from my Form1, and I am showing WorkingForm? Also, can this be implemented cleaner?
private void button14_Click(object sender, EventArgs e)
{
List<object> param = new List<object>();
object[] objectparams = new object[1];
objectparams[0] = null;
Opera opera = new Opera();
System.Reflection.MethodInfo clearOpera = opera.GetType().GetMethod("ClearOpera");
param.Add(clearOpera);
param.Add(opera);
param.Add(objectparams);
backgroundWorker1.RunWorkerAsync(param);
}
private void button2_Click_1(object sender, EventArgs e)
{
Browser.cancelPending = true;
}
private delegate void getnewform();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
mainForm main = new mainForm();
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
var variab = (bool)form.Invoke(new getnewform(main.AskForConfirmation));
List<object> param = e.Argument as List<object>;
List<object> result = new List<object>();
var method = param[0] as MethodInfo;
object[] parameters = param[2] as object[];
if (parameters[0] == null)
{
result.Add(method.Invoke(param[1], null));
result.Add(false);
}
else
{
result.Add(method.Invoke(param[1], parameters));
if (parameters.Contains(true))
result.Add(true);
}
int progress = (100 * Browser.progressValue) / Browser.progressMax;
backgroundWorker1.ReportProgress(progress);
// If the BackgroundWorker.CancellationPending property is true, cancel
if (backgroundWorker1.CancellationPending)
{
Console.WriteLine("Cancelled");
Browser.cancelPending = true;
}
e.Result = result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
form.progressBar1.Value = e.ProgressPercentage;
form.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
Application.DoEvents();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
List<object> param = e.Result as List<object>;
if (e.Cancelled == false && param.Contains(true))
{
Display.DisplayURLs(param[0] as SortableBindingList<URL>);
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
MessageBox.Show("Done");
}
else if (e.Cancelled == false && param.Contains(false))
{
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
MessageBox.Show("Done");
}
}
public class mainForm
{
public void AskForConfirmation()
{
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
var workingForm = new TestURLGUI4.WorkingForm();
workingForm.ShowDialog(form);
workingForm.DialogResult = DialogResult.None;
}
}
Edit:
Ok, I have updated my code according to the suggestions, and now, this produces a stackoverflowexception in System.Windows.Forms.dll:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
mainForm main = new mainForm();
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
List<object> param = e.Argument as List<object>;
List<object> result = new List<object>();
var method = param[0] as MethodInfo;
object[] parameters = param[2] as object[];
if (parameters[0] == null)
{
result.Add(method.Invoke(param[1], null));
result.Add(false);
}
else
{
result.Add(method.Invoke(param[1], parameters));
if (parameters.Contains(true))
result.Add(true);
}
int progress = (100 * Browser.progressValue) / Browser.progressMax;
backgroundWorker1.ReportProgress(progress);
// If the BackgroundWorker.CancellationPending property is true, cancel
if (backgroundWorker1.CancellationPending)
{
Console.WriteLine("Cancelled");
Browser.cancelPending = true;
}
e.Result = result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestURLGUI4.Form1 form1 = (TestURLGUI4.Form1)Application.OpenForms[0];
if (Application.OpenForms.Count >= 2)
{
TestURLGUI4.WorkingForm form2 = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
form2.progressBar1.Value = e.ProgressPercentage;
form2.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
Application.DoEvents();
}
else if(Application.OpenForms.Count == 1)
{
var workingForm = new TestURLGUI4.WorkingForm();
workingForm.ShowDialog(form1);
}
}
The purpose of a BackgroundWorker is to invoke code on another thread (not the UI thread). By calling Invoke in the DoWork method, you're completely circumventing the purpose of BackgroundWorker. Do all your UI work before you start the worker. If you need to interact with the user while the worker is working, do it in the ProgressChanged handler--it runs on the UI thread and you don't need to use Invoke in ProgressChanged.
By invoking UI work in DoWork, you run the risk of a deadlock, which will hang your program
you cant run UI on other threads. Has to be on the main thread.
Instantiate the UI before you start the new thread. In the new thread use cross thread invoke methods on the controls you want to work with. Look here for example http://msdn.microsoft.com/en-us/library/ms171728.aspx