C# Update DataGridView from BackgroundWorker - c#

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;
}
}
}
}

Related

how to update the records from My DataTable within a BackgroundWorker and show the progress

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
}
}
}

What is the good way to stop a query reader in a BackgroundWorker?

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.

Progress bar taking 20 seconds to start when it should start right away after selecting a value of a combobox in C# WindowsForms

I am having an issue with my program. my program will let the user select a dropdown value and after they select the dropdown value the datagridview should load with the data from a table, and then the progress bar should start the percentage 1 to 100%. Now everything in my program works the datagridview loads correctly and everything else in the program. the progress bar also works and loads but the issue comes when the user selects the dropdow combobox the progress bar takes like 15 to 20 seconds to start. I would like it to right away.
Can you see my code and see what could be the issue to why the progress bar is not starting right away?
if you need more information please let me know.
namespace DatagridViewProgressBar
{
public partial class Form1 : Form
{
//datagridview, bindingsource, data_apapter global objects variables
private DataGridView dataGridView = new DataGridView();
private BindingSource bindingSource = new BindingSource();
private SqlDataAdapter dataAdapter = new SqlDataAdapter();
DataTable dt = new DataTable();
//class objects
Databases lemars = new Databases();
Databases schuyler = new Databases();
Databases detroitlakeskc = new Databases();
public Form1()
{
InitializeComponent();
// To report progress from the background worker we set this property
dbWorker = new BackgroundWorker();
dbWorker.DoWork += new DoWorkEventHandler(dbWorker_DoWork);
dbWorker.ProgressChanged += new ProgressChangedEventHandler(dbWorker_ProgressChanged);
dbWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(dbWorker_RunWorkerCompleted);
dbWorker.WorkerReportsProgress = true;
dbWorker.WorkerSupportsCancellation = true;
}
private void btn_Exit_Click(object sender, EventArgs e)
{
this.Close();
}
private void comboBox_Database_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox_Database.SelectedItem.ToString() == "LeMars21St")
{
if (dbWorker.IsBusy != true)
{
dbWorker.RunWorkerAsync();
}
}
}
private void GetTableToDataGridView()
{
//prgBar_DataGridViewLoading
DatabaseColumns Obj = new DatabaseColumns();
String SqlcmdString = #"SELECT invoice, shipment, Project, invoiceDateTB, CreatedDate, typeName, exportedDate, statusName, total, import_status, Time_Completed, ERROR_DESCRIPTION FROM dbo.AllInvoicesInReadyStatus";
SqlDataReader reader;
int progress;
using (SqlConnection conn = new SqlConnection(lemars._LeMarsConnectionString))
{
reader = null;
SqlCommand Sqlcmd = new SqlCommand(SqlcmdString, conn);
conn.Open();
reader = Sqlcmd.ExecuteReader();
if (reader.HasRows)
{
try
{
dt.Load(reader);
for (int i = 0; i < dt.Rows.Count; i++)
{
Obj.Invoice = dt.Rows[i]["invoice"].ToString();
Obj.Shipment = dt.Rows[i]["shipment"].ToString();
Obj.Project = dt.Rows[i]["Project"].ToString();
Obj.InvoiceDateTB = Convert.ToDateTime(dt.Rows[i]["invoiceDateTB"]);
Obj.CreatedDate = Convert.ToDateTime(dt.Rows[i]["CreatedDate"]);
Obj.TypeName = dt.Rows[i]["typeName"].ToString();
Obj.ExportedDate = Convert.ToDateTime(dt.Rows[i]["exportedDate"]);
Obj.StatusName = dt.Rows[i]["statusName"].ToString();
Obj.Total = Convert.ToDecimal(dt.Rows[i]["total"]);
Obj.ImportStatus = dt.Rows[i]["import_status"].ToString();
if (!Convert.IsDBNull(dt.Rows[i]["Time_Completed"]))
{
Obj.TimeCompleted = Convert.ToDateTime(dt.Rows[i]["Time_Completed"]);
}
Obj.ErrorDescription = dt.Rows[i]["ERROR_DESCRIPTION"].ToString();
progress = i * 100 / dt.Rows.Count;
dbWorker.ReportProgress(progress);
Thread.Sleep(500);
}
}
finally
{
conn.Close();
}
}
}
}
private void dbWorker_DoWork(object sender, DoWorkEventArgs e)
{
GetTableToDataGridView();
dbWorker.ReportProgress(100);
}
private void dbWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar_GetTasks.Value = e.ProgressPercentage;
// eg: Set your label text to the current value of the progress bar
lbl_PercentageCount.Text = (progressBar_GetTasks.Value.ToString() + "%");
}
private void dbWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
dataGridView_ShowAllData.DataSource = dt;
if (e.Cancelled)
{
MessageBox.Show("Process Cancelled.");
}
else if (e.Error != null)
{
MessageBox.Show("Error occurred: " + e.Error.Message);
}
else
{
MessageBox.Show("Successful Completion.");
}
//progressBar_GetTasks.Value = 0;
}
private void btn_CancelOperation_Click(object sender, EventArgs e)
{
if (dbWorker.IsBusy)
{
dbWorker.CancelAsync();
}
}
}
}
Doing dt.Load(reader); waits for the data to fully load before continuing, Get rid of that line and replace it with a while(reader.Read()) loop.
private void GetTableToDataGridView()
{
//prgBar_DataGridViewLoading
DatabaseColumns Obj = new DatabaseColumns();
String SqlcmdString = #"SELECT invoice, shipment, Project, invoiceDateTB, CreatedDate, typeName, exportedDate, statusName, total, import_status, Time_Completed, ERROR_DESCRIPTION FROM dbo.AllInvoicesInReadyStatus";
String CountcmdString = #"SELECT count(*) FROM dbo.AllInvoicesInReadyStatus";
SqlDataReader reader;
int progress;
int total;
using (SqlConnection conn = new SqlConnection(lemars._LeMarsConnectionString))
{
reader = null;
SqlCommand Sqlcmd = new SqlCommand(CountcmdString , conn);
conn.Open();
total = (int)Sqlcmd.ExecuteScalar(); //Get the total count.
Sqlcmd.CommandText = SqlcmdString;
using(reader = Sqlcmd.ExecuteReader()) //this should be in a using statement
{
while(reader.Read())
{
object[] row = new object[reader.VisibleFieldCount];
reader.GetValues(row);
LoadSingleRowInToTable(dt, row); //I leave this to you to write.
//You can just read directly from the reader.
Obj.Invoice = reader["invoice"].ToString();
Obj.Shipment = reader["shipment"].ToString();
Obj.Project = reader["Project"].ToString();
Obj.InvoiceDateTB = Convert.ToDateTime(reader["invoiceDateTB"]);
Obj.CreatedDate = Convert.ToDateTime(reader["CreatedDate"]);
Obj.TypeName = reader["typeName"].ToString();
Obj.ExportedDate = Convert.ToDateTime(reader["exportedDate"]);
Obj.StatusName = reader["statusName"].ToString();
Obj.Total = Convert.ToDecimal(reader["total"]);
Obj.ImportStatus = reader["import_status"].ToString();
if (!Convert.IsDBNull(reader["Time_Completed"]))
{
Obj.TimeCompleted = Convert.ToDateTime(reader["Time_Completed"]);
}
Obj.ErrorDescription = reader["ERROR_DESCRIPTION"].ToString();
//Only call report progress when the progress value changes.
var newProgress = i * 100 / total;
if(progress != newProgress)
{
progress = newProgress;
dbWorker.ReportProgress(progress);
}
//Thread.Sleep(500);
}
}
}
}
UPDATE: Here is a example based on Steve's deleted answer that shows a better solution without using a DataTable.
private void dbWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<DatabaseColumns> data = GetTableToList();
if (data == null) //data will be null if we canceled.
{
e.Cancel = true;
}
else
{
e.Result = data;
}
dbWorker.ReportProgress(100);
}
private void dbWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Process Cancelled.");
}
else if (e.Error != null)
{
MessageBox.Show("Error occurred: " + e.Error.Message);
}
else
{
dataGridView_ShowAllData.DataSource = e.Result; //use the result from thebackground worker, only use if not canceled or errored.
MessageBox.Show("Successful Completion.");
}
//progressBar_GetTasks.Value = 0;
}
private List<DatabaseColumns> GetTableToList()
{
List<DatabaseColumns> data = new List<DatabaseColumns>();
//prgBar_DataGridViewLoading
String SqlcmdString = #"SELECT invoice, shipment, Project, invoiceDateTB, CreatedDate, typeName, exportedDate, statusName, total, import_status, Time_Completed, ERROR_DESCRIPTION FROM dbo.AllInvoicesInReadyStatus";
String CountcmdString = #"SELECT count(*) FROM dbo.AllInvoicesInReadyStatus";
using (SqlConnection conn = new SqlConnection(lemars._LeMarsConnectionString))
{
SqlCommand Sqlcmd = new SqlCommand(CountcmdString, conn);
conn.Open();
var total = (int)Sqlcmd.ExecuteScalar();
Sqlcmd.CommandText = SqlcmdString;
int i = 0;
int progress = 0;
using (SqlDataReader reader = Sqlcmd.ExecuteReader()) //this should be in a using statement
{
while (reader.Read())
{
if (dbWorker.CancellationPending)
{
//Exit early if operation was canceled.
return null;
}
DatabaseColumns Obj = new DatabaseColumns();
//You can just read directly from the reader.
Obj.Invoice = reader["invoice"].ToString();
Obj.Shipment = reader["shipment"].ToString();
Obj.Project = reader["Project"].ToString();
Obj.InvoiceDateTB = Convert.ToDateTime(reader["invoiceDateTB"]);
Obj.CreatedDate = Convert.ToDateTime(reader["CreatedDate"]);
Obj.TypeName = reader["typeName"].ToString();
Obj.ExportedDate = Convert.ToDateTime(reader["exportedDate"]);
Obj.StatusName = reader["statusName"].ToString();
Obj.Total = Convert.ToDecimal(reader["total"]);
Obj.ImportStatus = reader["import_status"].ToString();
if (!Convert.IsDBNull(reader["Time_Completed"]))
{
Obj.TimeCompleted = Convert.ToDateTime(reader["Time_Completed"]);
}
Obj.ErrorDescription = reader["ERROR_DESCRIPTION"].ToString();
//Add the object to the list.
data.Add(Obj);
//Only call report progress when the progress value changes.
var newProgress = i * 100 / total;
if (progress != newProgress)
{
progress = newProgress;
dbWorker.ReportProgress(progress);
}
i++;
}
}
}
return data;
}
Something I had when doing my first backgroundWorker, was the GUI locking up. I made the mistake of trying to hand out every result (primnumbers in a range from 2 to selected bound) as it was found via report progress. If I had a long enough operation (it did not happen if the operation was short) I ended up overloading and locking up the GUI with write operations, making it appear I never actually added multithreading.
Now I noticed this line:
progress = i * 100 / dt.Rows.Count;
You run the progress code (and update the bar) every loop itteration, even if the percentage did not actually change. If you process 1 million itterations, that is 10000 redraws without the value actually changing. This might slow down later as hits become rarer or the GC starts interferring with optimal performance of the task so the GUI thread has "time to catch up".
You should check if the value actually changed, before writing it to the GUI. Latest in the ProgressReporting Event. But it might be possible to do something back in the loop itself.
I made some example code to showcase what I call the "GUI write overhead" problem:
using System;
using System.Windows.Forms;
namespace UIWriteOverhead
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int[] getNumbers(int upperLimit)
{
int[] ReturnValue = new int[upperLimit];
for (int i = 0; i < ReturnValue.Length; i++)
ReturnValue[i] = i;
return ReturnValue;
}
void printWithBuffer(int[] Values)
{
textBox1.Text = "";
string buffer = "";
foreach (int Number in Values)
buffer += Number.ToString() + Environment.NewLine;
textBox1.Text = buffer;
}
void printDirectly(int[] Values){
textBox1.Text = "";
foreach (int Number in Values)
textBox1.Text += Number.ToString() + Environment.NewLine;
}
private void btnPrintBuffer_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(10000);
MessageBox.Show("Printing with buffer");
printWithBuffer(temp);
MessageBox.Show("Printing done");
}
private void btnPrintDirect_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(1000);
MessageBox.Show("Printing directly");
printDirectly(temp);
MessageBox.Show("Printing done");
}
}
}

backgroundWorker or Thread Cell ProgressBar read DLL And Show treeView

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.

How to start a long running process in a separate thread

ok here we go last part almost done! one error to go hmmmmm.
This is using the suggestion and is complaining about delegates i think.
using System;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox2.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
Task t = new Task(() => GetsalesFigures(accCollection.Text));
t.Start();
}
private void SetPictureBoxVisibility(bool IsVisible)
{
if (pictureBox2.InvokeRequired)
{
pictureBox2.Invoke(new Action<bool>(SetPictureBoxVisibility), new Object[] { IsVisible });
}
else
{
pictureBox2.Visible = IsVisible;
}
}
private void SetCheckBoxValue(bool IsChecked)
{
if (checkBox1.InvokeRequired)
{
pictureBox2.Invoke(new Action<bool>(SetCheckBoxValue), new Object[] { IsChecked });
}
else
{
checkBox1.Checked = IsChecked;
}
}
private void AddItem(string value)
{
if (accCollection.InvokeRequired)
{
accCollection.Invoke(new Action<string>(AddItem), new Object[] { value });
}
else
{
accCollection.Items.Add(value);
}
}
private void Form1_Load(object sender, EventArgs e)
{
AutofillAccounts();
}
private void GetsalesFigures(string Acct)
{
try
{
string myConn = "Server=af" +
"Database=sdfta;" +
"uid=busdf4;" +
"pwd=drsdft;" +
"Connect Tisdf=120;";
string acct;// test using 1560
SqlConnection conn = new SqlConnection(myConn);
SqlCommand Pareto = new SqlCommand();
BindingSource bindme = new BindingSource();
SqlDataAdapter adapt1 = new SqlDataAdapter(Pareto);
DataSet dataSet1 = new DataSet();
DataTable table1 = new DataTable();
//CREATE THE THREAD
//acct = accCollection.Text;
acct = Acct;
string fromDate = this.dateTimePicker1.Value.ToString("MM/dd/yyyy");
string tooDate = this.dateTimePicker2.Value.ToString("MM/dd/yyyy");
Pareto.Connection = conn;
Pareto.CommandType = CommandType.StoredProcedure;
Pareto.CommandText = "dbo.GetSalesParetotemp";
Pareto.CommandTimeout = 120;
Pareto.Parameters.AddWithValue("#acct", acct);
Pareto.Parameters.AddWithValue("#from", fromDate);
Pareto.Parameters.AddWithValue("#too", tooDate);
//pictureBox2.Visible = true;
//checkBox1.Checked = true;
SetCheckBoxValue(true);
SetPictureBoxVisibility(true);
adapt1.Fill(dataSet1, "Pareto");
//checkBox1.Checked = false;
//pictureBox2.Visible = false;
SetCheckBoxValue(false);
SetPictureBoxVisibility(true);
this.dataGridView1.AutoGenerateColumns = true;
this.dataGridView1.DataSource = dataSet1;
this.dataGridView1.DataMember = "Pareto";
dataGridView1.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.AllCells);
SetPictureBoxVisibility(true);
acct = Acct;
}
catch (Exception execc)
{
MessageBox.Show("Whoops! Seems we couldnt connect to the server!"
+ " information:\n\n" + execc.Message + execc.StackTrace,
"Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
private void AutofillAccounts()
{
//get customers list and fill combo box on form load.
try
{
string myConn1 = "Server=sdf33;" +
"Database=sdft;" +
"uid=bdf4;" +
"pwd=ddft;" +
"Connect Timeout=6000;";
SqlConnection conn1 = new SqlConnection(myConn1);
conn1.Open();
SqlCommand accountFill = new SqlCommand("SELECT keycode FROM dbo.Customer", conn1);
SqlDataReader readacc = accountFill.ExecuteReader();
while (readacc.Read())
{
AddItem(readacc.GetString(0).ToString());
}
conn1.Close();
}
catch(Exception exc1)
{
MessageBox.Show("Whoops! Seems we couldnt connect to the server!"
+ " information:\n\n" + exc1.Message + exc1.StackTrace,
"Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
}
}
not sure where the changes should be made, shouldnt it be in the account method now?
You have many problems in your code:
The call to Application.Run() is a blocking call. It won't return until you close your form.
So your thread starts when your application is about to exit.
Just start your thread before calling Run():
Thread aniSql = new Thread(new ThreadStart(getSalesFigures));
aniSql.Start();
Application.Run(new Form1());
A Tip: Start using the debugger and step through your code. You would have noticed that your thread function is never reached.
You create a second instance of Form1 in the function GetSalesFigures. You have to use the existing instance when polling the state of the checkbox.
Your thread will perform only a single check and then it returns. You have to write some code in GetSalesFigures to wait for the user to check the checkbox. Otherwise nothing will happen.
You can use a ManualResetEvent for waiting on an event:
ManualResetEvent mre = new ManualResetEvent(false);
void YourThreadFunc() {
// Wait until someone signals mre
mre.WaiteOne();
// start sql
...
}
In your form:
private void button1_Click(object sender, EventArgs e)
{
// trigger the WaitHandle to signal the waiting thread
mre.Set();
}
Fixed some minor spelling errors, didn't have a compiler at hand back then.
To your last error: Just provide the accCollection.Text as parameter to your method. See the updated button1_Click() and GetsalesFigures(String Acct).
This is how my Partial Class : Form looks like
public Form1()
{
InitializeComponent();
pictureBox2.Visible = false;
}
private void Form1_Load(object sender, EventArgs e)
{
AutofillAccounts();
}
private void button1_Click(object sender, EventArgs e)
{
checkBox1.Checked = true;
string acct = accCollection.Text;
Task t = new Task(() => GetsalesFigures(acct));
t.Start();
}
private void GetsalesFigures(String Acct)
{
// (...)
//pictureBox2.Visible = true; use SetPictureBoxVisibility
SetPictureBoxVisibility(true);
//checkBox1.Checked = true; use SetCheckBoxValue
SetCheckBoxValue(true);
// (...)
SetCheckBoxValue(false);
SetPictureBoxVisibility(false);
// (...)
acct = Acct;
// (...)
SetDataGrid(true, dataSet1, "Pareto", DataGridViewAutoSizeColumnsMode.AllCells);
}
private void AutofillAccounts()
{
// (...)
while (readacc.Read())
{
AddItem(readacc.GetString(0).ToString());
}
}
private void SetCheckBoxValue(bool IsChecked)
{
if (checkBox1.InvokeRequired)
{
pictureBox2.Invoke(new Action<bool>(SetCheckBoxValue), new Object[] { IsChecked });
}
else
{
checkBox1.Checked = IsChecked;
}
}
private void SetPictureBoxVisibility(bool IsVisible)
{
if (pictureBox2.InvokeRequired)
{
pictureBox2.Invoke(new Action<bool>(SetPictureBoxVisibility), new Object[] { IsVisible });
}
else
{
pictureBox2.Visible = IsVisible;
}
}
// Your latest comment
private void AddItem(string value)
{
if (accCollection.InvokeRequired)
{
accCollection.Invoke(new Action<string>(AddItem), new Object[] { value });
}
else
{
accCollection.Items.Add(value);
}
}
private void SetDataGrid(bool AutoGenerateColumns, Object DataSource, String DataMember, DataGridViewAutoSizeColumnsMode Mode)
{
if (this.dataGridView1.InvokeRequired)
{
this.dataGridView1.Invoke(new Action<bool, Object, String, DataGridViewAutoSizeColumnsMode>(SetDataGrid),
AutoGenerateColumns, DataSource, DataMember, Mode);
}
else
{
this.dataGridView1.AutoGenerateColumns = AutoGenerateColumns;
this.dataGridView1.DataSource = DataSource;
this.dataGridView1.DataMember = DataMember;
dataGridView1.AutoResizeColumns(Mode);
}
}
And Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
To summary your problem: You have to encapsulate all control-related updates you want to call from another thread than the main thread just like I did it with the two controls.
Some ressources you might want to check for multithreading / tasks
http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx
http://blogs.msdn.com/b/pfxteam/archive/2009/06/30/9809774.aspx

Categories