C# Button with multiple events - c#

I am trying to overwrite a content in an label several times by always clicking the same button. Unfortunately, I only know how to override it once.
The problem I am facing is that the data in the label are from an SQL database and it only displays the data with ID = 1 in the label.
This is my code:
MySqlConnection conn = new MySqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString); // Connectionstring to the database
public MainWindow()
{
InitializeComponent();
}
private void btContinue_Click(object sender, RoutedEventArgs e)
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand("SELECT l_question from l_liescale", conn);
MySqlDataAdapter adp = new MySqlDataAdapter(cmd);
DataSet ds = new DataSet();
lbquestion.Content = cmd.ExecuteScalar(); //here I get the data into the label
}
catch (MySqlException ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
conn.Close();
}
}
}
Is there a way to display every data record from the database in one label and always overwriting it with the next record by clicking the button?

You should use ExecuteReader() instead of ExecuteScalar() to retrieve collection of data.
StringBuilder sb = new StringBuilder();
using(var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var question = reader[0].ToString();
sb.AppendFormat("Q: {0}\n", question); // use any other format if needed
}
}
lbquestion.Content = sb.ToString();
But the better way is to use ItemsControl or ListBox or other list-controls.
If you want to iterate by clicking the button you can retrieve all records to the memory and then iterate it in the event handler:
private readonly List<string> _questions;
private int currentIndex = -1;
public MainWindow()
{
InitializeComponent();
_questions = GetQuestionsByDataReader();
}
private void btContinue_Click(object sender, RoutedEventArgs e)
{
if(currentIndex < _questions.Count)
{
lbquestion.Content = _questions[++currentIndex];
}
}

Related

Sending data between 2 forms

I have form1 with datagridview and a button. When i click a button, a new form opens up where there is a textbox and also a button. In this textbox i can write query and with a click of a button query results are shown in form1 datagridview. The problem is that it opens up another instance of form1 , but i would like that form1 stays open the whole time and only records in datagridview are changing, according to the query input in form2. Both form1 and form2 needs to be opened and active when called.
Here is my code:
//FORM 1
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var queryForm = new Form2();
queryForm.Show(this);
}
//FORM 2
public Form2()
{
InitializeComponent();
}
private SqlConnection Conn;
private void Form1_Load(object sender, EventArgs e)
{
Conn = new SqlConnection(#"Data Source=srvr;Initial Catalog =db; User ID =user; Password =pass");
}
private void btnExecute_Click(object sender, EventArgs e)
{
Form1 frm1 = new Form1();
frm1.Show(this);
frm1.Activate();
SqlCommand cmd = new SqlCommand();
cmd.Connection = Conn;
cmd.CommandText = txtQuery.Text;
try
{
Conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
frm1.dataGridView1.Columns.Clear();
frm1.dataGridView1.Rows.Clear();
if (reader.HasRows)
{
DataTable schema = reader.GetSchemaTable();
int field_num = 0;
foreach (DataRow schema_row in schema.Rows)
{
int col_num = frm1.dataGridView1.Columns.Add(
"col" + field_num.ToString(),
schema_row.Field<string>("ColumnName"));
field_num++;
frm1.dataGridView1.Columns[col_num].AutoSizeMode =
DataGridViewAutoSizeColumnMode.AllCells;
}
object[] values = new object[reader.FieldCount];
while (reader.Read())
{
reader.GetValues(values);
frm1.dataGridView1.Rows.Add(values);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error executing command.\n" + ex.Message);
}
finally
{
Conn.Close();
}
}
Well, since you are calling Form1 frm1 = new Form1(); - what else did you expect than opens up another instance of form1 ? - Why should new Form1() not produce another instance?
You will need to get the reference of the already created Form1.
See this for example
Find the open forms in c# windows application
When you found it you can activate it for example:
var frm1 = Application.OpenForms[0];
//frm1.Show(this); <- don't need to call Show since its already open
frm1.Activate();
Also you should change your btnExecute_Click to this.
private void btnExecute_Click(object sender, EventArgs e)
{
var frm1 = Application.OpenForms[0] as Form1; //find `Form1` like you want, I only take [0]
//always create a new instance of SqlConnection here and dispose it with the using Keyword
//don't use a private field to try to keep the Connection, let the internal Connection pool handle that case
using (var con = new SqlConnection(#"Data Source=srvr;Initial Catalog =db; User ID =user; Password =pass"))
{
try
{
con.Open();
//clean up, Command/Reader with using keyword
using (var cmd = con.CreateCommand())
{
cmd.CommandText = txtQuery.Text;
using (SqlDataReader reader = cmd.ExecuteReader())
{
//read data
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error executing command.\n" + ex.Message);
}
}
//should activate the `Form1` AFTER the job is done, you can consider if you only want to activate it if the previous Code didn't fail
frm1.Activate();
}
Don't really get what you are doing in your "read_data" routine.
This Code block:
frm1.dataGridView1.Columns.Clear();
frm1.dataGridView1.Rows.Clear();
if (reader.HasRows)
{
DataTable schema = reader.GetSchemaTable();
int field_num = 0;
foreach (DataRow schema_row in schema.Rows)
{
int col_num = frm1.dataGridView1.Columns.Add(
"col" + field_num.ToString(),
schema_row.Field<string>("ColumnName"));
field_num++;
frm1.dataGridView1.Columns[col_num].AutoSizeMode =
DataGridViewAutoSizeColumnMode.AllCells;
}
object[] values = new object[reader.FieldCount];
while (reader.Read())
{
reader.GetValues(values);
frm1.dataGridView1.Rows.Add(values);
}
}
Try if the following is sufficient, replace my comment "//read data" in the above code with this:
frm1.dataGridView1.AutoGenerateColumns = true; //say to automatically create columns, based on the result inside the datatable
frm1.dataGridView1.Columns.Clear();
var dataTable = new DataTable();
dataTable.Load(dataReader); //load the SqlDataReader into the DataTable
frm1.dataGridView1.DataSource = dataTable; //set the dataGridView's DataSource to the dataTable
On button click in form1, you can simply open a new instance of form2 and do your work there and on closing receive that value in form1. Or you can pass the instance of your form1 into form2 via constructor and update form1 from form2. For example:
var isFormClosed = false;
using(form1 frm = new form1())
{
// do something here
frm.ShowDialog();
isFormClosed = true;
}
Or, if you prefer to pass the reference of form1 into form2,
var isFormClosed = false;
using(form1 frm = new form1(this))
{
// do something here
frm.ShowDialog();
isFormClosed = true;
}
Here, in form2, you can simply use the passed reference of form1 to update properties or grids.

Keep additional rows when DBConcurrencyException is raised

I made a simple application that displays data from a database in a DataGridView, users can add rows, delete rows, update values and save the changes.
Now, let's say User A modifies a value in row 8 and saves. User B adds 50 rows and wants to modify a cell in row 8 also. When user B saves, DBConcurrencyException occurs and all his work is lost.
Considering the way people will use this app, this scenario should not happen but there is still a small chance.
Is it possible to keep the added rows when the DBConcurrencyException is raised ? Or should I just tell the users to save as often as possible ?
Here is the relevant code :
private BindingSource bindingSource = null;
private SqlCommandBuilder commandBuilder = null;
string conStringLocal = "xxxxxxxxxxx";
SqlCommand command;
SqlDataAdapter dataAdapter;
DataTable dataTable = new DataTable();
public Form1()
{
InitializeComponent();
DataBind();
}
private void DataBind()
{
dataGridViewCND.DataSource = null;
dataTable.Clear();
string query = "SELECT * FROM myTable";
SqlConnection con = new SqlConnection(conStringLocal);
try
{
con.Open();
command = con.CreateCommand();
command.CommandText = query;
dataAdapter = new SqlDataAdapter(query, con);
commandBuilder = new SqlCommandBuilder(dataAdapter);
dataAdapter.Fill(dataTable);
bindingSource = new BindingSource { DataSource = dataTable };
dataGridViewCND.DataSource = bindingSource;
this.dataGridViewCND.Columns["id"].Visible = false;
this.dataGridViewCND.Sort(this.dataGridViewCND.Columns["Date"], ListSortDirection.Ascending);
}
catch (Exception ex)
{
// GENERIC ERROR MESSAGE
}
}
private void buttonSave_Click(object sender, EventArgs e)
{
try
{
dataGridViewCND.EndEdit();
dataAdapter.Update(dataTable);
DataBind();
// UPDATE SUCCESS MESSAGE
}
catch(DBConcurrencyException ex)
{
// CONCURRENCY ERROR MESSAGE
DataBind();
}
catch (Exception ex)
{
// GENERIC ERROR MESSAGE
DataBind();
}
}

Page_Load() load data don't go away

I am working on a visual web part that does simple CRUD operations and I have this strange behavior in Page_load().
I grab first record from query and assign some text fields when page is loaded. When I clear the form and update the form with new different inputs, those text fields remember the first values and ignores newly entered data.
Am I missing anything in Page_load() when I display data when the page is loaded?
public partial class VisualWebPart1UserControl : UserControl
{
string connstr = AdminDashBoard.Utility.GetConnectionString();
private DataSet sqlDst = new DataSet();
private static int RowNo = 0;
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(connstr);
string strQuery = "xxxxxxxxxxxxxxxxxxxxxx";
try
{
conn.Open();
SqlCommand sqlCmd = new SqlCommand(strQuery, conn);
sqlCmd.CommandType = CommandType.Text;
SqlDataAdapter sqlAdap = new SqlDataAdapter(sqlCmd);
sqlAdap.Fill(sqlDst);
//these fields remember first assigned data!!!
this.TextBox1.Text = sqlDst.Tables[0].Rows[RowNo][0].ToString();
this.TextBox2.Text = sqlDst.Tables[0].Rows[RowNo][1].ToString();
this.TextBox3.Text = sqlDst.Tables[0].Rows[RowNo][2].ToString();
this.DateTimeControl1.SelectedDate = Convert.ToDateTime(sqlDst.Tables[0].Rows[RowNo][3].ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
conn.Close();
conn.Dispose();
}
}
thanks in advance

Update mysql database from datagridview selected row

i have a datagridview which loads mysql database table t_pi_clients on form load event,and i have another tab which contains textboxes of the respective columns of t_pi_client, which am able to get data from fullrowselect mode into those textboxes. now i want to update the database upon changes in the those textbox values. so far i've tried some process and gets my "entry saved" messageBox.show but nothing happens to database, so am hoping someone could help me out maybe am missing something thanks
public partial class frmMain : Form
{
MySqlConnection connection;
MySqlDataAdapter mySqlDataAdapter;
DataSet dt = new DataSet();
DataSet DS = new DataSet();
DataSet dg = new DataSet();
public frmMain()
{
InitializeComponent();
}
#region Main load
private void frmMain_Load(object sender, EventArgs e)
{
var connectionString = ConfigurationManager.ConnectionStrings["Pigen"].ConnectionString;
connection = new MySqlConnection(connectionString);
if (this.OpenConnection() == true)
{
mySqlDataAdapter = new MySqlDataAdapter("select * from t_pi_Clients", connection);
DataSet DS = new DataSet();
mySqlDataAdapter.Fill(DS);
kryptonDataGridView1.DataSource = DS.Tables[0];
kryptonDataGridView1.Columns[0].Visible = false;
mySqlDataAdapter = new MySqlDataAdapter("select * from t_pi_msg_charge_Rate", connection);
DataSet dt = new DataSet();
mySqlDataAdapter.Fill(dt);
kryptonDataGridView2.DataSource = dt.Tables[0];
mySqlDataAdapter = new MySqlDataAdapter("select * from t_pi_client_deposits", connection);
DataSet dg = new DataSet();
mySqlDataAdapter.Fill(dg);
kryptonDataGridView3.DataSource = dg.Tables[0];
}
}
//loads selected row data into textboxes
private void kryptonDataGridView1_DoubleClick(object sender, EventArgs e)
{
textboxClientCode.Text = kryptonDataGridView1.SelectedRows[0].Cells["ClientCode"].Value.ToString();
txtboxClientName.Text = kryptonDataGridView1.SelectedRows[0].Cells["ClientName"].Value.ToString();
txtboxPostalAddress.Text = kryptonDataGridView1.SelectedRows[0].Cells["PostalAdd"].Value.ToString();
txtboxTelephone.Text = kryptonDataGridView1.SelectedRows[0].Cells["Telephone"].Value.ToString();
txtboxFax.Text = kryptonDataGridView1.SelectedRows[0].Cells["Fax"].Value.ToString();
txtboxEmailAddress1.Text = kryptonDataGridView1.SelectedRows[0].Cells["EmailAdd1"].Value.ToString();
txtboxEmailAddress2.Text = kryptonDataGridView1.SelectedRows[0].Cells["EmailAdd2"].Value.ToString();
txtboxEmailAddress3.Text = kryptonDataGridView1.SelectedRows[0].Cells["EmailAdd3"].Value.ToString();
txtboxWebsite.Text = kryptonDataGridView1.SelectedRows[0].Cells["Website"].Value.ToString();
txtboxChargeRate.Text = kryptonDataGridView1.SelectedRows[0].Cells["ChargeRate"].Value.ToString();
txtboxTotalDepo.Text = kryptonDataGridView1.SelectedRows[0].Cells["TotalDeposit"].Value.ToString();
txtboxAccountBal.Text = kryptonDataGridView1.SelectedRows[0].Cells["AccountBal"].Value.ToString();
txtboxEntrydate.Text = kryptonDataGridView1.SelectedRows[0].Cells["EntryDate"].Value.ToString();
}
now i tried this method to update but doesn't update database
private void kryptonbtnUpdate_Click(object sender, EventArgs e)
{
var connectionString = ConfigurationManager.ConnectionStrings["Pigen"].ConnectionString;
using (MySqlConnection Conn = new MySqlConnection(connectionString))
if (Conn.State.ToString() != "Open")
{
}
else
{
connection.Open();
}
try
{
DataTable changes = ((DataTable)kryptonDataGridView1.DataSource).GetChanges();
if (changes != null)
{
MySqlCommandBuilder mcb = new MySqlCommandBuilder(mySqlDataAdapter);
mySqlDataAdapter.UpdateCommand = mcb.GetUpdateCommand();
mySqlDataAdapter.Update(changes);
((DataTable)kryptonDataGridView1.DataSource).AcceptChanges();
mySqlDataAdapter.Update(DS);
}
// adapter.Update(rowsToUpdate);
// mySqlDataAdapter.Update(DS);
MessageBox.Show("Entry Saved");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This is just a pseudocode of what you need to do
string cmdText = #"UPDATE t_pi_Clients
SET ClientName = #ClientName,
PostalAdd = #PostalAdd,
Telephone = #Telephone,
Fax = #Fax,
.... etc ....
WHERE ClientCode = #ClientCode";
using(MySqlConnection cn = new MySqlConnection(.....))
using(MySqlCommand cmd = new MySqlCommand(cmdText, cn))
{
cn.Open();
cmd.Parameters.AddWithValue("#ClientName", txtboxClientName.Text);
cmd.Parameters.AddWithValue("#PostalAdd", txtboxPostalAddress.Text);
....etc etc...
cmd.Parameters.AddWithValue("#ClientCode", textboxClientCode.Text);
int rowsUpdated = cmd.ExecuteNonQuery();
if(rowsUpdated > 0)
{
// extract the code that loads DataGridView1 from the Form_Load
// and create a reusable method that you could call from here
}
}
First you build an sql command text with the UPDATE clause. I assume that your primary key (the field that uniquely identifies your records) is the ClientCode field.
Then create the connection and the command. Fill the command parameters collection with the parameters required by your text taking the values from the TextBoxes.
Call the ExecuteNonQuery to store the values.
If you succeed then you need to update or reload your datagridview. The best approach would be setting one by one the gridview cells of the current row with the new values from the textboxes, or you could simply extract the code used in form_load to fill the grid and make a new method that you could call from the button click event. (But this could be slower if you have many records)

ComboBox taking 2 columns, displaying 1st using 2nd with Thread.Task

I've made a previous post that tried to use a textbox. From this I found out you can simply add an sql query results (the excute reader) to the ComboBox and then display and use the other column value.
Problem I have is I'm using a task for my form that runs a different HUGE sql query so it does not lock up my controls in my form. The problem, in detail, is that I'm using an invoke method wrapped around that control that only gets the 1st column.
public void fillmycombo()
{
SqlConnection conn1 = new SqlConnection(myConn1);
conn1.Open();
if (string.Compare(_userName, admin) == 0)
{
SqlCommand accountFill = new SqlCommand("SELECT name, FROM dbo.Customer", conn1);
SqlDataReader readacc = accountFill.ExecuteReader();
while (readacc.Read())
{
AddItem(readacc.GetString(0).ToString());
//accCollection.DataSource = readacc;
//accCollection.DisplayMember = "name";
//accCollection.ValueMember = "keycode";
}
conn1.Close();
}
}
this method as you can see gets the name.
private void AddItem(string value)
{
if (accCollection.InvokeRequired)
{
accCollection.Invoke(new Action<string>(AddItem), new Object[] { value });
}
else
{
accCollection.Items.Add(value);
}
}
as you can see im using the invoke method to wrap the control for use in my method that is on the task.
private void button1_Click_1(object sender, EventArgs e)
{
checkBox1.Checked = true;
string acct = accCollection.Text;
Task t = new Task(() => GetsalesFigures(acct));
t.Start();
}
this runs the task that calls my giant query method.
private void getsalesfigures(string acct)
{
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();
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);
SetCheckBoxValue(true);
SetPictureBoxVisibility(true);
adapt1.Fill(dataSet1, "Pareto");
SetCheckBoxValue(false);
SetPictureBoxVisibility(false);
SetDataGrid(true, dataSet1, "Pareto", DataGridViewAutoSizeColumnsMode.AllCells);
dataGridView1.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.AllCells);
}
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);
}
}
What I want to do is add another field to my query called "keycode", store this in a 2nd column in my ComboBox and then display the name field for the user, but use the keycode field as the value to be used in my giant task query.
I'm having trouble figuring out how I to do this.
In the past, I've used an object that contains an override of ToString() and instead of adding plain strings to my combo boxes (or other lists), I add these objects. Then, when you need to get the value of a selected item, you can cast it and do GetValue(). Here's a sample.
class LookupTableItem {
private string Text;
private object Value;
public LookupTableItem(string Text, object Value) {
this.Text = Text;
this.Value = Value;
}
public override string ToString() {
return Text;
}
public object GetValue() {
return Value;
}
}
Then, change your AddItem to add items this way:
accCollection.Items.Add(new LookupTableItem(text, value));
And to retrieve the value:
((LookupTableItem)accCollection.Items[0]).GetValue();

Categories