I have a windows application using SqlDependency. This application represents a log monitor UI get the latest rows added in a specific table in the database and view it in a DataGridView
The code is realize the change in SQL Server but the DataGridView show nothing (No errors, and no data, just as many line as in datatable)
Here is my code
public partial class Form1 : Form
{
private int changeCount = 0;
private const string tableName = "NhanVien";
private const string statusMessage = "Đã có {0} thay đổi.";
//' The following objects are reused
//' for the lifetime of the application.
private SqlConnection connection = null;
private SqlCommand command = null;
private DataSet dataToWatch = null;
public Form1()
{
InitializeComponent();
Form2 frm = new Form2();
frm.Show();
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch (Exception ex)
{
return false;
}
}
private string GetConnectionString()
{
// To avoid storing the connection string in your code,
// you can retrive it from a configuration file.
// Return "Data Source=THU-PC\TINTIN;Initial Catalog=QLVT;Persist Security Info=True;User ID=sa;Password=kc;Pooling = false"
return "Data Source=MSI;Initial Catalog=ChuyenDeCNPM;User ID=sa;Password=123;";
}
private string GetSQL()
{
return "select manv as [Mã NV],Ho as [ Họ],Ten as [Tên],phai as [Phái],diachi as [ Địa chỉ] from dbo.NhanVien";
}
private void nhanVienBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
this.nhanVienBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.chuyenDeCNPMDataSet);
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'chuyenDeCNPMDataSet.NhanVien' table. You can move, or remove it, as needed.
//this.nhanVienTableAdapter.Fill(this.chuyenDeCNPMDataSet.NhanVien);
if (CanRequestNotifications() == true)
BatDau();
else
MessageBox.Show("Bạn chưa kích hoạt dịch vụ Broker", "Cảnh báo", MessageBoxButtons.OK);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// It is illegal to update the UI from a worker thread
// The following code checks to see if it is safe update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch
OnChangeEventHandler tempDelegate = new OnChangeEventHandler(dependency_OnChange);
object[] args = new[] { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(tempDelegate, args);
return;
}
// Remove the handler since it's only good
// for a single notification
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
// At this point, the code is executing on the
// UI thread, so it is safe to update the UI.
++changeCount;
this.label1.Text = string.Format(statusMessage, changeCount);
// Add information from the event arguments to the list box
// for debugging purposes only.
{
var withBlock = this.ListBox1.Items;
withBlock.Clear();
withBlock.Add("Info: " + e.Info.ToString());
withBlock.Add("Source: " + e.Source.ToString());
withBlock.Add("Type: " + e.Type.ToString());
}
// Reload the dataset that's bound to the grid.
GetData();
}
private void GetData()
{
// Empty the dataset so that there is only
// one batch worth of data displayed.
dataToWatch.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataToWatch, tableName);
DataGridView1.DataSource = null;
DataGridView1.DataSource = dataToWatch;
DataGridView1.DataMember = tableName;
}
/*DataTable dt = new DataTable();
dt.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
DataGridView1.DataSource = dt;*/
}
private void BatDau()
{
changeCount = 0;
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop(GetConnectionString());
try
{
SqlDependency.Start(GetConnectionString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
if (connection == null)
{
connection = new SqlConnection(GetConnectionString());
connection.Open();
}
if (command == null)
// GetSQL is a local procedure that returns
// a paramaterized SQL string. You might want
// to use a stored procedure in your application.
command = new SqlCommand(GetSQL(), connection);
if (dataToWatch == null)
dataToWatch = new DataSet();
GetData();
}
private void button_update_Click(object sender, EventArgs e)
{
Update form = new Update();
form.Show();
}
private void Form1_FormClosed(System.Object sender, System.Windows.Forms.FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (connection != null)
connection.Close();
}
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void button_exit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
This is my datagridview after start
I tried debug and saw that the datasource is set. But it's still empty in GridView
I've just found out that i have TableAdapter and BindingSource connect with GridView [![like this][1]][1], so it's cause fault. If you delete all that, use datatable instead of dataset, the code will be like this:
private void GetData()
{
dt.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
dt.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
DataGridView1.DataSource = dt;
}
You should declare DataTable as global variable. Everything should work well. Good Luck!
[1]: https://i.stack.imgur.com/aODka.png
Related
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.
I am converting an old Outlook VBA macro I created to a C# VSTO Add-in. This tool is for bulk archiving of emails to various public folders and a local .pst file and needs to be able to find emails based on a user's search criteria. I had a whale of a time getting the AdvancedSearchComplete event to trigger in VBA, but eventually got it to work. Of course, I am now have the same problem in the C# VSTO add-in.
To use the tool, the user will input their criteria on a userform, then click the Find Emails button to perform the search with the results populating a datagridview.
public partial class Email_Archiver : Form
{
public Email_Archiver()
{
InitializeComponent();
//Add event handlers
Globals.ThisAddIn.Application.AdvancedSearchComplete += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_AdvancedSearchCompleteEventHandler(Application_AdvancedSearchComplete);
}
public void Application_AdvancedSearchComplete(Microsoft.Office.Interop.Outlook.Search SearchObject)
{ //Handles AdvancedSearchComplete event
if (SearchObject.Tag == "Archiver Search")
{
OutlookFunctions.SearchComplete = true;
MessageBox.Show(this, "Search Complete!","Email Search Error",MessageBoxButtons.OK);
}
}
private void Find_Button_Click(object sender, EventArgs e)
{
//Set datagridview datasource
EmailList.DataSource = FindEmails();
EmailList.Columns["EntryID"].Visible = false;
}
public DataTable FindEmails()
{
var dt = new DataTable();
//Format DataTable
dt.Columns.Add("EntryID", typeof(string));
dt.Columns.Add("Project No.", typeof(int));
dt.Columns.Add("Subject", typeof(string));
dt.Columns.Add("Sender", typeof(string));
dt.Columns.Add("Recipient", typeof(string));
dt.Columns.Add("Time Sent", typeof(DateTime));
dt.Columns.Add("Size", typeof(decimal));
//Do stuff to get "searchFolders" and "searchCriteria" from form
//...
dt = OutlookFunctions.RunAdvancedSearch(searchFolders, searchCriteria);
return dt;
}
}
class OutlookFunctions
{
public static bool SearchComplete;
public static DataTable RunAdvancedSearch(string[] searchFolders, string[] searchCriteria)
{
//Get Outlook namespace
var oApp = Globals.ThisAddIn.Application;
var oNS = oApp.GetNamespace("mapi");
Microsoft.Office.Interop.Outlook.Search advSearch = null;
//Do other stuff to properly set up the scope and filter
//...
//Perform search
SearchComplete = false;
try
{
advSearch = oApp.AdvancedSearch(scope, filter, false, "Archiver Search");
}
catch (System.Exception ex)
{
MessageBox.Show("An error has occurred during the search for emails. \n \n"
+ ex.Message);
return null;
}
Stopwatch timer = new Stopwatch();
timer.Start();
while (!SearchComplete && timer.Elapsed.TotalSeconds < 10)
{
//Do nothing
}
if (!SearchComplete)
{
advSearch.Stop();
}
if (advSearch != null)
{
var resultTable = new DataTable();
//Send results to resultTable
return resultTable;
}
return null;
}
}
The search works and I get the proper results, but I had to add in the timer to make things move along, otherwise the code never finishes because AdvancedSearchComplete isn't triggered. I still get all of the emails I'm looking for and doing essentially the same search just in the Outlook explorer, I get the same results in a fraction of a second.
After the timer ends the search and the datagridview is filled with the results, THEN the AdvancedSearchComplete fires and I get my "Search Complete!" message.
What am I missing here that is keeping the event from triggering to end the search (in a timely fashion)?
You could refer to the following code about using backgroundwork:
BackgroundWorker bw = new BackgroundWorker();
private void button1_Click(object sender, EventArgs e)
{
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//
// Boring.... Do your long work
//
System.Threading.Thread.Sleep(20000);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.InvokeRequired)
{
//Hide your form here
this.Invoke(new MethodInvoker(delegate { this.Close(); }));
}
}
It appears that any code following the initiation for the AdvancedSearch method will essentially end the search without triggering the AdvancedSearchComplete or AdvancedSearchStopped events.
Instead, I now have the following in the ThisAddIn class:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
//Create task pane object
archiverTaskPane1 = new ArchiverTaskPane();
customTaskPane = this.CustomTaskPanes.Add(archiverTaskPane1, "Title");
//Add event handler for opening task pane
customTaskPane.VisibleChanged += new EventHandler(customTaskPane_VisibleChanged);
//Add event handler for Outlook's AdvancedSearch
Globals.ThisAddIn.Application.AdvancedSearchComplete += new Outlook.ApplicationEvents_11_AdvancedSearchCompleteEventHandler(Application_AdvancedSearchComplete);
}
public void Application_AdvancedSearchComplete(Microsoft.Office.Interop.Outlook.Search SearchObject)
{
DataTable dt = new DataTable();
OutlookFunctions oFunctions = new OutlookFunctions();
dt = oFunctions.BuildResults(SearchObject.Results);
this.archiverTaskPane1.DataGridFiller(dt);
}
In the ArchiverTaskPane Class (formerly Email_Archiver Form - I REALLY liked the Task Pane idea)
private void Find_Button_Click(object sender, EventArgs e)
{
FindEmails();
}
public void FindEmails()
{
DataTable dt = new DataTable();
//Add columns to DataTable
//Do stuff to get "searchFolders" and "searchCriteria" from form
//...
//Create OutlookFunctions instance
OutlookFunctions oFunctions = new OutlookFunctions();
oFunctions.RunAdvancedSearch(searchFolders, searchCriteria);
}
public void DataGridFiller(DataTable results)
{
EmailList.DataSource = results;
}
In the OutlookFunctions Class:
public static DataTable RunAdvancedSearch(string[] searchFolders, string[] searchCriteria)
{
//Get Outlook namespace
var oApp = Globals.ThisAddIn.Application;
var oNS = oApp.GetNamespace("mapi");
Microsoft.Office.Interop.Outlook.Search advSearch = null;
//Do other stuff to properly set up the scope and filter
//...
//Perform search
SearchComplete = false;
try
{
advSearch = oApp.AdvancedSearch(scope, filter, false, "Archiver Search");
}
catch (System.Exception ex)
{
MessageBox.Show("An error has occurred during the search for emails. \n \n"
+ ex.Message);
}
}
public DataTable BuildResults(Results searchResults)
{
DataTable resultTable = new DataTable();
//Do stuff to build DataTable from search results
//...
return resultTable;
}
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 combobox and want to save the changes in the database.
What I want to do is if the combobox is selected and it is true it must run the code below. If it is false it must then skip the code and go further.
The code below is checking if the combobox is enabled. But when I'm compiling it's saying true when not selected
private void Log()
{
if (kaartburgerlijkestand.Enabled)
{
veranderingBurgelijkestaat();
}
}
The code below is saving the data in the database
private string veranderingBurgerlijkestaat()
{
string gebruiker = curMedewerker.Behandelnaam;
string bekeken = prodermaform.pKaart();
string tabblad = tabControl1.SelectedTab.Text;
string pat = CPatient.GeefPatientNaam(patient.Id);
string wijz = "Burgerlijkestaat: " + kaartBurgerlijkestand.Text;
CDb dcon = new CDb();
try
{
if (dcon.Open())
{
SqlCommand cmd = new SqlCommand("INSERT INTO dbo.loggen(Gebruiker, Bekeken, Tabblad, Patientnaam, Wijziging, Datum) VALUES(#gebruiker, #bekeken, #tabblad, #pat, #wijz, #datum)", dcon.Conn);
cmd.Parameters.AddWithValue("#gebruiker", gebruiker);
cmd.Parameters.AddWithValue("#bekeken", bekeken);
cmd.Parameters.AddWithValue("#tabblad", tabblad);
cmd.Parameters.AddWithValue("#pat", pat);
cmd.Parameters.AddWithValue("#wijz", wijz);
cmd.Parameters.AddWithValue("#datum", DateTime.Now);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
dcon.Close();
}
return wijz;
}
Could someone show me a example how to do it
I Found the solution
I have made a check
private void doCheck(object sender, EventArgs e)
{
cmbox = false;
if (kaartBurgerlijkestaat.Focused)
{
veranderingBurgerlijkestand();
}
cmbox = true;
}
Then I used the SelectedValueChanged Event
private void kaartBurgerlijkestand_SelectedValueChanged(object sender, EventArgs e)
{
if (cmbox)
doCheck(sender, e);
}
And it works fine.
I want to thank you all for helping me!
Add an Event Listener, the kaartburgerlijkestand.Enabled only check your combobox is enabled to select or not.
Add this line after your InitializeComponent(); line of Form.cs file
kaartburgerlijkestand.SelectedIndexChanged += new System.EventHandler
(this.kaartburgerlijkestand_SelectedIndexChanged);
Also add a function for above code :
private void kaartburgerlijkestand_SelectedIndexChanged(object sender, EventArgs e)
{
veranderingBurgelijkestand();
}
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