sync data grid view - c#

i have data grid view that its data source is a data table in form in c#,
how can i make it read table from database continuously
i mean if my program running in many computers in same network and connected with same database , if computer 1 add row to the database its appears automatically in computer 2 without clicking any button to refresh.
void load()
{
c.connect("sel_dep");
c.com.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da=new SqlDataAdapter (c.com);
DataTable dt = new DataTable();
c.open();
int last = 0;
while (true)
{
if (dt.Rows.Count > 0)
dt.Rows.Clear();
da.Fill(dt);
dd = dt;
if (dt.Rows.Count != last)
{
last = dt.Rows.Count;
this.Invoke((MethodInvoker)delegate { dataGridView1.DataSource = dt; dataGridView1.SelectedRows[0].Selected = true; label1.Text = dataGridView1.RowCount.ToString(); });
}
}
c.close();
}
private void Form3_Load(object sender, EventArgs e)
{
aa = new Thread(() => { load(); });
aa.Start();
}
this is my tray

If you are using Winforms to create the desktop application, you would not need timer.
Simply drag and drop Timer control from the Toolbox on your form, in design mode.
' The form load should be altered as below
private void Form3_Load(object sender, EventArgs e)
{
' Assume the Timer control is named as 'oTimer'
oTimer.Enabled = true;
oTimer.Interval = 60000; ' In Milliseconds, equates to 1 minute
oTimer.Start();
}
Create the Tick event for the Timer. This event would be fired each time the interval elapses.
private void oTimer_Tick(object sender, EventArgs e)
{
<Call your function to initiate/refresh the DataGrid.DataSource within this event>
}
In order to further understand how the Timer class works, refer Timer Class (System.Windows.Forms).
Also refer to Stackoverlow question Winforms Timer for Dummies with plenty of resources and tips to master the Timer control.

If you're using Sql Server you can have a look at Query Notifications, more specifically at SqlDependency class. It can be used to get notifications from sql server, when data changes, into your desktop application
Built upon the Service Broker infrastructure, query notifications
allow applications to be notified when data has changed. This feature
is particularly useful for applications that provide a cache of
information from a database, such as a Web application, and need to be
notified when the source data is changed.

Related

Asp.net running code ever 5 min with timer

I get the following error on page_load:
c# no argument given that corresponds to the required formal parameter
Why is this?
protected void Page_Load(object sender, EventArgs e)
{
timerevent();
}
DataSet _dataSet = new DataSet();
private DataTable _dataTable;
string _sqlQuery = #"show slave status;";
string _sqlStop = #"stop slave;";
string _sqlStart = #"start slave;";
System.Timers.Timer mytimer = new System.Timers.Timer();
private void SetTimer()
{
mytimer.Elapsed += new ElapsedEventHandler(timerevent);
mytimer.Interval = 1000;
}
private void timerevent(object sender, ElapsedEventArgs e)
{
BindData();
}
public void BindData()
{
mytimer.Start();
}
Did you stop to consider the lifecycle of a Page? Think about it.
An instance of your Page class is created when an HTTP request comes in. Once the page has gone through all of its lifecycle events, an HTTP response is sent to the client. At that point, the Page instance will be destroyed. It will go away, taking any state stored in it (such as field containing a timer) with it.
For these reasons, the approach you're trying simply will not work.
If you want to update the client's DOM update every 5 minutes, you'll either have to have some client side code (JavaScript) that contains a timer and initiates a new HTTP request, or use tech like web sockets (SignalR) to push changes. If you do choose a SignalR approach, you can have a separate service with scheduling functionality (perhaps using Hangfire or Quartz) to initiate the push.

Why does windows form freeze when it loads in C#?

I am new to C# and I am using windows forms.
I am building an application and I am facing a strange serious problem when Form loads.
I have 2 forms:
Form1 has a button_Edit and DataGridView
Form2 has a DataGridView1 and DataGridView2
As it is shown in the code and the screen shot, in Form1 when I select a row in DataGridView then click on Button_Edit the Order number and DateTime values in DataGridView on Form1 are passed to Form2 and then Form2 opens up.
Now in Form2 Load event there is a SQL query which takes Order number and DateTime to bring the relevant order details and then fill DataGridView1 and DataGridView2 in Form2.
In Form1:
Form2 frm2 = new Form2();
private void button_Edit_Click(object sender, EventArgs e)
{
frm2._ Order_Number= Convert.ToInt32(dataGridView1.SelectedRows[0].Cells[0].Value);
frm2._ Date_Time= Convert.ToDateTime(dataGridView1.SelectedRows[0].Cells[4].Value);
frm2.ShowDialog();
}
In Form2:
SqlConnection MyConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
SqlCommand MyCommand = new SqlCommand();
DataTable DataTable = new DataTable();
SqlDataAdapter Sql_Data_Adapter = new SqlDataAdapter();
int Order_Number;
DateTime Date_Time;
int i;
double Sum;
int RowIndex;
public int _ Order_Number
{
set { Order_Number = value; }
}
public DateTime _ Date_Time
{
set { Date_Time = value; }
}
private void Form2_Load(object sender, EventArgs e)
{
DataTable.Rows.Clear();
DataTable.Columns.Clear();
MyConnection.Open();
MyCommand.CommandText = "SELECT * FROM Customer_Order_Details WHERE Order_Number = #OrderNumber and Date_Time = #DateTime ";
MyCommand.Connection = MyConnection;
MyCommand.Parameters.Add("#OrderNumber", SqlDbType.Int).Value = Order_Number;
MyCommand.Parameters.Add("#DateTime", SqlDbType.DateTime).Value = Date_Time;
Sql_Data_Adapter.SelectCommand = MyCommand;
Sql_Data_Adapter.Fill(DataTable);
MyCommand.Parameters.Clear();
MyConnection.Close();
dataGridView1.Rows.Clear();
dataGridView2.Rows[0].Cells[1].Value = 0;
Sum = 0;
//////////////FILL THE ORDER INTO DATAGRIDVIEW1///////////
RowIndex = DataTable.Rows.Count - 1;
for (i = 0; i <= RowIndex; i++)
{
dataGridView1.Rows.Add(DataTable.Rows[i][2], DataTable.Rows[i][3], DataTable.Rows[i][4]);
// Calculate the total:
Sum = Convert.ToDouble(DataTable.Rows[i][4]) + Sum;
}
dataGridView2.Rows[0].Cells[1].Value = sum;
}
The issue:
This code works fine and as I wanted and DataGridView1 & DataGridView2 in Form2 are filled with the right details and it works fine when Form2 loads.
However, sometimes Form2 freezes after filling both DataGridView1 & DataGridView2 when Form2 loads and I can not do anything until I kill the Application using Task manager.
This issue happens sometimes and it is unpredictable. I really don’t know what is wrong.
I had a look here , here and here but non of those questions related to my issue. Note that I already use try catch and it does not throw anything because the form freezes. This freeze behavior occurs in the release mode I mean after I build EXE file then I install it in a PC and here the issue happens.
Has anyone got any idea why this bad unpredictable behavior occurs?
Is there anything that I should change in my code?
I will be very happy to listen to any new ideas/solutions no matter how small it is, it will be very beneficial.
Thank you
Do SQL work on another thread. Check out Asynchronous call.
BeginInvoke
I suggest using Async versions of Open (connecting to RDBMS) and ExecuteReader (query executing):
con.Open() -> await con.OpenAsync()
q.ExecuteReader() -> await q.ExecuteReaderAsync()
this easy substitution makes UI responsible (it doesn't freeze) while connecting to RDBMS and executing the query.
// do not forget to mark Form2_Load method as async
private async void Form2_Load(object sender, EventArgs e) {
// Do not share the connection - create it and dispose for each call
using (SqlConnection con = new SqlConnection(...)) {
await con.OpenAsync();
string sql =
#"SELECT *
FROM Customer_Order_Details
WHERE Order_Number = #OrderNumber
AND Date_Time = #DateTime";
// do not share command/query as well
using (SqlCommand q = new SqlCommand(sql, con)) {
q.Parameters.Add("#OrderNumber", SqlDbType.Int).Value = Order_Number;
q.Parameters.Add("#DateTime", SqlDbType.DateTime).Value = Date_Time;
dataGridView1.Rows.Clear();
dataGridView2.Rows[0].Cells[1].Value = 0;
Sum = 0;
// We actually don't want any sql adapter:
// all we have to do is to fetach data from cursor and append it to grids
using (var reader = await q.ExecuteReaderAsync()) {
while (reader.Read()) {
dataGridView1.Rows.Add(reader[2], reader[3], reader[4]);
Sum += Convert.ToDouble(reader[4]);
}
}
}
}
}
as said the code snippet you gave us isn't well written. You should use more Try/Catch Statements to prevent crashs and freezes on your programm. This will help you to find bugs even while running the Programm in release. Furthermore you have many options to solve your problem.
1:
Try to start Form2 in a second thread, then only your form2 will freeze until your sql finishes
2:
As mentioned before try to use asyncronous calls to avoid freeze time of handling the sql
3:
There is no direct need of a SQL Database/Connection. You could also use a Collection and create objects defined of your Products (like cola) and them bind them through the database. (If you are interested in i could show you a example)
The best way for you would be:
Add some Try/Catch Statements and get familiar with it, get familiar with Systems.Thread and try to start your form2 in a new thread if this doenst work go to step 2 and add asynchronus calls
In the end i would like to tell you, that namen your Forms "Form1" and "Form2" isn't nice could maybe you like to change it.

Update DataGridView or update the binded DataSource?

I am working on a C# project in which a DataGridView is shown in the WinForm at runtime and needs to be updated every 2~3 seconds. The DataSrouce of each DataGridView is binding to a DataSet which could be changed according to users's operation.
As I am totally a newbie to C#, I have no idea how should I achieve a real-time refresh of the forms in the GUI. Of course I need multi-threading so that the update of the data shown could be done in background and the GUI does not hang.
PS: In Qt, each view is bounded to a model and I can update each model in a different thread from the main GUI thread so that the GUI will not stuck. This is what I am now doing. Sample codes below.
// MainForm.cs
public Bind()
{
dataGridView.DataSource = TableCollection.DataTable1;
}
public StartTimer()
{
System.Windows.Forms.Timer tdfTimer = new System.Windows.Forms.Timer();
guiTimer.Tick += new EventHandler(Refresh);
guiTimer.Interval = 500;
guiTimer.Start();
}
public void Refresh(object sender, EventArgs e)
{
if (backgroundwork == null)
{
backgroundwork = new BackgroundWorker();
backgroundwork.DoWork += delegate(object s, DoWorkEventArgs args) { TableCollection.UpdateData(); };
}
if (!backgroundwork.IsBusy)
backgroundwork.RunWorkerAsync();
}
// TableCollection.cs
class TableCollection
{
static DataTable dataTable1;
public DataTable1
{
get { return dataTable1; }
}
static public void UpdateData()
{
// here i update each row in dataTable1
}
}
you want to update the datagridview frequently so you have to create BindingSource for that.the code should look like
BindingSource DGSource = new BindingSource(TableCollection.DataTable1, null);
dataGridView2.DataSource = DGSource;

Display loading message until complete SQL data retrieve

I'm using WeifenLuo dockpanel-suit.
I need to show some kind of splash form or loading message before SQL complets loading data.
What I've tried didn't work
public partial class frmPostventa : DockContent
{
private void frmPostventa_Load(object sender, EventArgs e)
{
bool done = false;
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new SplashForm())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
//Database task heavy load
cargarDatos();
done = true;
}
public void cargarDatos()
{
string strSQL = "exec SAI_ALGO.spPostventa";
SqlDataAdapter dataAdapter = new SqlDataAdapter(strSQL, strCon);
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter);
// Populate a new data table and bind it to the BindingSource.
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
dataAdapter.Fill(table);
bindingSource1.DataSource = table;
}
}
EDIT: Added CargarDatos()
DoEvents() nearly always leads to a very bad place, and should pretty much always be avoided. The problem with this particular implementation is that your long running process (cargarDatos()) is going to block the UI thread until it completes, so DoEvents() will have no effect; it will not interrupt a method while it is executing (assuming cargarDatos() isn't doing something internally that would yield the thread). Also, creating and interacting with a UI control from some background thread is not valid here.
You should use a BackgroundWorker to perform the long running operation and raise status updates to the UI: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

SqlDependency using BackgroundWorker

I have a table in a SQL Server database that represents a log file of some actions inserted from a running windows service. Everything is working well.
But, I have a Windows application that gets the latest rows that have been inserted in the log table and views it in a DataGridView. While developing this application I depended on Using SqlDependency in a Windows Application from MSDN. It is working well, but when the log table receives a large number of log details, the Windows app hangs up and the main thread pool becomes too busy.
I want to run the same code referenced in the previous link in a separated thread pool by using Thread class or BackgroundWorker control. This means a thread for using UI controls and another one for listening to the database changes and get it into the DataGridView.
You can see the UI screenshot from this link "UI"
No. (1): This GroupBox represents the UI tools which users can use it while monitoring.
No. (2): The Start button is responsible for beginning to listen and receive updates from the database and refill the DataGridView.
No. (3): This grid represents the new logs that have been inserted in the database.
No. (4): This number (38 changes) represents the count of listening of sql dependency to the database changes.
My code:
public partial class frmMain : Form
{
SqlConnection conn;
const string tableName = "OutgoingLog";
const string statusMessage = "{0} changes have occurred.";
int changeCount = 0;
private static DataSet dataToWatch = null;
private static SqlConnection connection = null;
private static SqlCommand command = null;
public frmMain()
{
InitializeComponent();
}
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
{
return false;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// Updating the UI from a worker thread is not permitted.
// The following code checks to see if it is safe to
// 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 = { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(tempDelegate, args);
return;
}
// Remove the handler, since it is 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;
lblChanges.Text = String.Format(statusMessage, changeCount);
this.Refresh();
// Reload the dataset that is bound to the grid.
GetData();
}
private void GetData()
{
// Empty the dataset so that there is only
// one batch 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);
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;
dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1;
}
}
private void btnStart_Click(object sender, EventArgs e)
{
changeCount = 0;
lblChanges.Text = String.Format(statusMessage, changeCount);
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop("<my connection string>");
SqlDependency.Start("<my connection string>");
if (connection == null)
{
connection = new SqlConnection("<my connection string>");
}
if (command == null)
{
command = new SqlCommand("select * from OutgoingLog", connection);
}
if (dataToWatch == null)
{
dataToWatch = new DataSet();
}
GetData();
}
private void frmMain_Load(object sender, EventArgs e)
{
btnStart.Enabled = CanRequestNotifications();
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
SqlDependency.Stop("<my connection string>");
}
}
What I want exactly: when user click the Start button, the application run the code in a separated thread pool.
First if I understand well the change notification is already executed on another thread so using one more threading layer should be useless.
Indeed what causes the application to hang is the update of the UI, on the UI thread.
Could you show the code responsible for this update please?
If there is a lot of notifications visual update will be longer and you can't do much:
update the grid by chunks to smooth the update: instead of inserting 1000 new records you run 10 updates of 100 records, but you take the risk of being overwhelmed by data if you don't process them fast enough
using a collection that handles notification natively like a BindingList could help
Moreover what you can do to enhance the user experience, avoiding the unpleasant "it hangs" effect, is displaying a progress bar or a simple spinner.
UPDATE:
So if the first part of the GetData function is the bottleneck then indeed you could use another thread, e.g. from the thread-pool:
private void GetData()
{
// Start the retrieval of data on another thread to let the UI thread free
ThreadPool.QueueUserWorkItem(o =>
{
// Empty the dataset so that there is only
// one batch 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);
// Update the UI
dgv.Invoke(() =>
{
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;
dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1;
});
}
});
}
So the only part that will run on the UI thread is the update of the datagrid.
Not tested but hope this helps...
LAST? UPDATE:
With some synchronization to avoid concurrent execution:
AutoResetEvent running = new AutoResetEvent(true);
private void GetData()
{
// Start the retrieval of data on another thread to let the UI thread free
ThreadPool.QueueUserWorkItem(o =>
{
running.WaitOne();
// Empty the dataset so that there is only
// one batch 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);
running.Set();
// Update the UI
dgv.Invoke(() =>
{
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;
dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1;
});
}
});
}

Categories