Why does windows form freeze when it loads in C#? - 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.

Related

Array doesn't keep it's value outside of the method it's filled in

I have this line of code that gets the ids of a table and feeds them to an int array, called idPuntos (Note: This method is sandwiched between the Open() and Close() methods somewhere else, so it's not something about the database in that sense).
public void refreshIds()
{
idInit.Clear();
try
{
SqlCommand getIds = new SqlCommand("SELECT id_punto FROM Puntos", cnn);
SqlDataReader idsReader = getIds.ExecuteReader();
while (idsReader.Read())
{
//idInit is a global ArrayList
idInit.Add(idsReader[0]);
}
//idPuntos is a global array, declared without value
idPuntos = (int[])idInit.ToArray(typeof(int));
//This line works here, I test printed it and got the values I wanted, yet it doesn't work in the following method...
bajaId = idPuntos[0];
}
catch (SqlException)
{
statusbajas.Text = "Hubo un error al recuperar los datos.";
}
}
It won't work here:
private void listabajas_SelectedIndexChanged(object sender, EventArgs e)
{
//it doesnt work here, and i dont understand why
bajaId = idPuntos[0];
//statusbajas.Text = "es " + listabajas.SelectedIndex.ToString();
}
Any ideas?
EDIT: By not working, I mean idPuntos[0] appears to be null while it isn't in the first, where it actually holds the value I feed it.
EDIT 2: The problem seems to start since the constructor. Putting a breakpoint on the first Close() call, made me realize the crash occurs since that point.
This is my constructor:
public AltasPuntos()
{
InitializeComponent();
str = Properties.Settings.Default.SWMConnString;
cnn = new SqlConnection(str);
cnn.Open();
refreshListNombres();
cnn.Close();
cnn.Open();
refreshIds();
cnn.Close();
}
You need to ensure that refreshIds is called before listabajas_SelectedIndexChanged gets fired.
If you're using WinForms, you can put a call to refreshIds into the constructor the the form.
That's about as much as I can suggest from the information provided in the question.

sync data grid view

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.

Why does the same function do 2 different things?

I'm an Amateur at C# and I don't understand what's going on here. It's not really a problem, as I can make a quickfix for it, but I still want to know Why.
Disclaimer: I know this is probably not the best way to design this program, but I have been given a very short amount of time to develop it and I'm just trying to ship it by the deadline.
I have a main form in my program. This form calls a second form when 1 of 2 buttons are pressed. These buttons use the same function to open the second form:
private void setupShow(int show)
{
fSetup setup = new fSetup(show);
setup.Show();
setup.FormClosed += new FormClosedEventHandler(setup_FormClosed);
}
When button1 calls that function, it hides the main form and opens the next form. However, when the second button calls that function it keeps the main form open, but still opens the second form.
So what could be causing this?
Button1 has quite a bit more code than button2 and if the functions are needed, I can post them here but it would fill up the majority of the screen.
Edit:
int show;
Is just a variable I use for functions performed in the second form. It has no bearing on anything as far as windows closing.
Button 1: Only calls this function. The function inserts into a database, then gets the inserted ID of that row and passes it to the second form
private void CheckFields()
{
OleDbCommand insertParty;
OleDbDataAdapter partyAdapt = new OleDbDataAdapter();
int nameL = PName.Text.Length;
int newPartyID = 0;
if (nameL > 0)
{
String test = "INSERT INTO Parties (PartyName, BackgroundImg) VALUES (?, ?)";
insertParty = new OleDbCommand(test, Parties);
insertParty.Parameters.AddWithValue("PartyName", PName.Text);
insertParty.Parameters.AddWithValue("BackgroundImg", tBrowse.Text);
Parties.Open();
insertParty.ExecuteNonQuery();
NewPartyForm.ActiveForm.Visible = false;
OleDbCommand selectnewParty;
OleDbDataAdapter newpartyAdaptr = new OleDbDataAdapter();
String selectNew = "SELECT TOP 1 PartyID, PartyName FROM Parties ORDER BY PartyID DESC";
selectnewParty = new OleDbCommand(selectNew, Parties);
OleDbDataReader newReader = selectnewParty.ExecuteReader();
while (newReader.Read())
{
newPartyID = newReader.GetInt32(0);
}
setupShow(newPartyID);
}
else
MessageBox.Show("Please Create a Party Name");
}
Button 2: Cuts out the integer from the string in a combobox and passes it to the second form
private void bLoad_Click(object sender, EventArgs e)
{
Object selectedParty = cLoadP.SelectedItem;
String sP = selectedParty.ToString();
String d1 = " - ";
char[] delim = d1.ToCharArray();
String[] numS = sP.Split(delim);
setupShow(Convert.ToInt32(numS[0]));
}
Like I said, this code is being developed by an amateur and very quickly. This is not the way I would normally do things, but I don't have much time to really think. lol
Second form initial function:
public fSetup(int partyID)
{
InitializeComponent();
pID = partyID;
lpID.Text += " " + pID.ToString();
}
I suspect that the cause of the different behavior is this line in the code for button 1:
NewPartyForm.ActiveForm.Visible = false;
There's no similar line in the code for button 2.

Use SqlDataAdapter to fill a table in a dataset with a new name, code don't run completely and no error

As you see, my code (it's in myform_load), when I run project, it continues to line 1. Just not run other lines after that. I got no error, but the code doesn't run completely. After it come to line one, myform is shown. Where is the problem?
SqlDataAdapter userSharj_history;
private void myform_Load(object sender, EventArgs e)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
using (userSharj_history = new SqlDataAdapter(String.Format("SELECT * FROM users_sharj WHERE user_id = {0} AND datetime BETWEEN '{1}%' AND '{2}%'", user_id, az_tarikh_globalizationDateTimePicker1.Text, ta_tarikh_globalizationDateTimePicker1.Text), con))
{
1. userSharj_history.Fill(nan_DataSet, "sharjes");
2. gridControl1.DataSource = nan_DataSet.Tables["sharjes"];
}
con.Close();
}
view_btn.Focus();
}
The Query string is fine. I tested it in GUI Query Builder.
I use this dataset in my main form that call this form and there is no table in it with the name sharjes.
Either check the Output window or enable reporting of all exceptions. Visual Studio will not inform you about many exceptions by default.
Go to menu Debug --> Exceptions... and then tick Thrown for Common language runtime Exceptions and click OK. Run your code again and see if it throws an exception now.

simple multithreaded c# exe to test scalability of SQL stored proc

I have an email application that will be called upon to deliver to the UI the number of new messages for a given user on each page load.
I have a few variations of things I am testing on the DB level but all are abstracted by the stored proc call.
I'm trying to slam the DB to see what the breaking point would be by creating a simple multi-threaded app to call the proc multiple times and show a timestamp of the results in a grid. Then I just scroll down the grid to see the duration from the first result to the last to determine how many results i can serve per second.
I think this because SQL should be able to handle hundreds of these calls per second but the time stamps show only 4 per second. This seems way too long to select a value based on an ID within a clustered index column.
In a nutshell, I have a table such as this userId, newMsgCount with a clustered index on userId. SQL should be able to server hundreds of these responses per second.
I think the laggard is my .NET app.
How can I make this a good test to achieve the test results based on SQL performance?
I have a textbox that takes a number of DB calls and a button to invoke the test, then a grid to show results.
private void btnSend_Click(object sender, EventArgs e)
{
int count = Convert.ToInt32(txtThreadCount.Text);
dtStatus = new DataTable();
dtStatus.Columns.Add(new DataColumn("Thread No."));
//dtStatus.Columns.Add("User Id");
dtStatus.Columns.Add("Count");
dtStatus.Columns.Add("time");
for (int i = 0; i < count; i++)
{
ThreadPool.QueueUserWorkItem(GetMessageCount, i);
}
MessageBox.Show("Results retrieved successfully. \n Please see the result tab.");
grdEmail.DataSource = dtStatus;
grdEmail.Refresh();
}
private static void GetMessageCount(object threadContext)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = new SqlConnection();
cmd.Connection.ConnectionString = "Data Source=server1;Initial Catalog=emails;Persist Security Info=True;User ID=IS_User;Password=M1";
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sel_Message_New_Count";
cmd.Parameters.AddWithValue("#UserId", 4);
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
da.SelectCommand = cmd;
da.Fill(ds);
if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
{
DataRow dr = dtStatus.NewRow();
dr[0] = dtStatus.Rows.Count + 1;
//dr[1] = 1;
dr[1] = ds.Tables[0].Rows[0][0];
dr[2] = DateTime.Now.ToString("hh:mm.ss:ffff");
dtStatus.Rows.Add(dr);
}
}
You may want to try changing the number of threads the ThreadPool is using. See the ThreadPool.SetMinThreads method.
You have massive memory leak issues. You need a using statement on SQLCommand, SQLConnection, SQLDataAdapter.
Don't use a DataSet, use a DataTable and dispose of it in a using statement.
Are you sure that DataTable.NewRow() is a thread safe construct whereby you can just slam it full of rows?
Why are you executing that code in a button click event? The button click event should spawn a background thread, which in turn executes the code you currently have in the button click event. The UI should then display a status bar indicating the progess of that background thread...
At the heart of your problem is the fact that you're not waiting for all those threadpool threads you spawn to finish. You start them, then immediately display a MessageBox.
You need to wait for each thread to finish.
How do you do that? Here's one solution - or - you could try using Parallel.For if you're using .NET 4 and the Task Parallel Library.

Categories