I have a table for Orders (orderTBL). When the user creates an order it adds a new row into the database with a custom order number. I load my treeview nodes from this database, however, if there is more than one row with the same order number, it creates more than one treeview node. Is it possible to display only one treeview node per order number? The TreeView is used to control a DataRowFilter to only display orders with the order number selected in a DataGridView This is the code I use :
public void ordersForm_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'lWADataBaseDataSet.orderTBL' table. You can move, or remove it, as needed.
// this.orderTBLTableAdapter.Fill(this.lWADataBaseDataSet.orderTBL);
getOrders();
getNumbers();
string sOrder = null;
int I = 0;
for (I = 0; (I <= (orderTBL.Rows.Count - 1)); I++)
{
sOrder = orderTBL.Rows[1][1].ToString();
treeView1.Nodes[0].Nodes.Add(sOrder);
}
}
private void getNumbers()
{
SqlCeConnection con = new SqlCeConnection(#"Data Source=|DataDirectory|\LWADataBase.sdf;");
try
{
con.Open();
}
catch (SqlCeException ex)
{
MessageBox.Show(ex.Message);
return;
}
treeView1.Nodes.Clear();
SqlCeCommand cmd = new SqlCeCommand("SELECT * FROM orderTBL ORDER BY[Order Number] ASC", con);
try
{
SqlCeDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
TreeNode node = new TreeNode(dr["Order Number"].ToString());
treeView1.Nodes.Add(node);
}
}
catch (SqlCeException ex)
{
MessageBox.Show(ex.Message);
return;
}
con.Close();
}
I am assuming that having more than one row with the same order # is part of your design. If not you should do a check on the database before insertion.
Being part of your design you just want to load one node for each order you can do this in two ways.
Filter the data via the query:
SqlCeCommand cmd = new SqlCeCommand("SELECT distinct [Order Number] FROM orderTBL ORDER BY[Order Number] ASC", con);
'distinct' tells the database to make sure now row in the return set is duplicated.
Or maintain a temp list during load that checks if you have loaded that order yet.
private void getNumbers()
{
SqlCeConnection con = new SqlCeConnection(#"Data Source=|DataDirectory|\LWADataBase.sdf;");
try
{
con.Open();
}
catch (SqlCeException ex)
{
MessageBox.Show(ex.Message);
return;
}
treeView1.Nodes.Clear();
SqlCeCommand cmd = new SqlCeCommand("SELECT * FROM orderTBL ORDER BY[Order Number] ASC", con);
try
{
//Temp List
List<string> ordersLoaded = new List<string>();
SqlCeDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
string oderNum = dr["Order Number"].ToString();
//Check if you loaded that order already
if (!ordersLoaded.Contains(oderNum))
{
//Add order to loaded list
ordersLoaded.Add(oderNum);
treeView1.Nodes.Add(new TreeNode(oderNum));
}
}
}
catch (SqlCeException ex)
{
MessageBox.Show(ex.Message);
return;
}
con.Close();
}
Related
This question already has answers here:
How do I delete multiple rows with different IDs?
(4 answers)
Closed 5 years ago.
I want to delete multiple rows or all rows in Database using DataGridView.
For example. If I have 10 rows in the DataGridView, then all the 10 rows should be selected and deleted. Here is my code for deleting single row in database using DataGridView.
private void btnDeleteProduct_Click(object sender, EventArgs e)
{
ConnectionStringSettings conSettings = ConfigurationManager.ConnectionStrings["MyDBConnectionString"];
string connectionString = conSettings.ConnectionString;
if (ProductServicesDataGrid.CurrentRow.Selected)
{
string selectedCode = ProductServicesDataGrid.CurrentRow.Cells[0].Value.ToString();
conn = new SqlConnection(connectionString);
try
{
conn.Open();
cmd = new SqlCommand("DELETE FROM ProductServices where ProductCode='" + selectedCode + "' ", conn);
sdr = cmd.ExecuteReader();
loadProductServicesTable();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
}
else
{
MessageBox.Show("Row is not Selected");
}
}
Can someone please help me and modify my code?
first, you shouldn't use CurrentRow property because it returns only one row. Instead. Use SelectedRows collection and iterate through it.
Second, NEVER concatenate parameters in command text. Instead, use parametrized query.
//connection.Open is moved out of the loop, to avoid unnecessary open/close
conn = new SqlConnection(connectionString);
conn.Open();
try
{
foreach (var row in ProductServicesDataGrid.SelectedRows)
{
string selectedCode = row.Cells[0].Value.ToString();
try
{
cmd = new SqlCommand("DELETE FROM ProductServices where ProductCode=#productCode", conn);
cmd.Parameters.Add(.Parameters.Add("productCode", SqlDbType.VarChar).Value = selectedCode;
sdr = cmd.ExecuteReader();
//this probably shouldn't be here, but outside the foreach loop.
//that way table will be loaded after deletion of those n rows.
//loadProductServicesTable();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
//refresh products after deletion
loadProductServicesTable();
I am iterating a incidentNoReserve tbl in SQL Server. On button click, if it matches a variable the entire row will be deleted from incidentNoReserve tbl.
This is the code:
private void button3_Click(object sender, EventArgs e)
{
try
{
using (var cmd106 = new SqlCommand("select * from [dbo].[incidentNoReserve]", cnn))
{
cnn.Open();
SqlDataReader reader = cmd106.ExecuteReader();
while (reader.Read())
{
var commonNo = reader["incidentNoReserveId"];
//Every new row will create a new dictionary that holds the columns
if (Convert.ToInt16(commonNo) == newLastIncidentNo )
{
var cmd107 = new SqlCommand("Delete from[dbo].[incidentNoReserve] where incidentNoReserveId = #newLastIncidentNo", cnn);
cmd107.Parameters.AddWithValue("#newLastIncidentNo", newLastIncidentNo);
cmd107.ExecuteNonQuery();
}
cnn.Close();
}
reader.Close();
}
}
catch (Exception ex)
{
// If an exception occurs, write it to the console
Console.WriteLine(ex.ToString());
}
finally
{
cnn.Close();
}
this.Close();
}
The newLastIncidentNo have 18 and while it reaches commonNo it have 10 which is only the first row in incidentNoReserve table.
What could be the problem?
You are closing the connection in the loop
And you are using the same connection to loop and update
it worked i have just removed the inner open and close connection.Thanks ALL
i fixed it like this :
while (reader.Read())
{
int commonNo = Convert.ToInt16(reader["incidentNoReserveId"]);
//Every new row will create a new dictionary that holds the columns
if (Convert.ToInt16(commonNo) == newLastIncidentNo)
{
reader.Close();
//newLastIncidentNo = (Convert.ToInt16(commonNo));
using (SqlCommand command = new SqlCommand("Delete from [dbo].[incidentNoReserve] where incidentNoReserveId = #newLastIncidentNo", cnn))
{
//MessageBox.Show(newLastIncidentNo.ToString());
command.Parameters.AddWithValue("#newLastIncidentNo", newLastIncidentNo);
command.ExecuteNonQuery();
}
}
}
cnn.Close();
}
}
catch (Exception ex)
With the code below, I can insert the selected values of the checkboxes into a single line in the database.In the same line, it is added the date, a note, and an Id number from another table (not unique). In my .aspx page there is a simple treenode-checkboxes structure.
Now, I want to insert each selected value from the checkbox in his own line in the database table.
For example: 3 checkboxes are selected: 3 lines with each own value inserted (plus the same date, note, Id for all of them) in the database.
Please any ideas how to do it?
protected void btn_vac_Click(object sender, EventArgs e)
{
int id = Convert.ToInt32(Session["pa_id"]);
string vn = string.Empty;
if (TreeView1.CheckedNodes.Count > 0)
{
foreach (TreeNode node in TreeView1.CheckedNodes)
{
vn += string.Format("{0}", node.Text);
}
}
try
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn.Open();
if (note_vac.Text.Length != 0)
{
string insert_emer = "insert into P_vaccines (V_name,P_Id,Note,Date) values (#vn,#p_id,#note,#Date) ";
SqlCommand com = new SqlCommand(insert_emer, conn);
com.Parameters.AddWithValue("#vn", string.Format("{0}", vn));
com.Parameters.AddWithValue("#p_id", id);
com.Parameters.AddWithValue("#note", note_vac.Text);
com.Parameters.AddWithValue("#Date", DateTime.Now.ToShortDateString());
com.ExecuteNonQuery();
}
else
{
string insert_emer = "insert into P_vaccines (V_name,P_Id,Date) values (#vn,#p_id,#Date) ";
SqlCommand com = new SqlCommand(insert_emer, conn);
com.Parameters.AddWithValue("#vn", string.Format("{0}", vn));
com.Parameters.AddWithValue("#p_id", id);
com.Parameters.AddWithValue("#Date", DateTime.Now.ToShortDateString());
com.ExecuteNonQuery();
}
Response.Write("<script>alert('Success!')</script>");
conn.Close();
}
catch (Exception ex)
{
Response.Write("Error :" + ex.ToString());
}
}
You code could be simplified a lot. First, you should prepare the command before entering the foreach loop over the checked indices. This means, creating the connection and the command then inside the loop just update the values of the parameters that need to change. Something like this.
protected void btn_vac_Click(object sender, EventArgs e)
{
if (TreeView1.CheckedNodes.Count > 0)
{
try
{
string insert_text = "insert into P_vaccines (V_name,P_Id,Note,[Date]) values (#vn,#p_id,#note,#Date) ";
using(SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString))
using(SqlCommand com = new SqlCommand(insert_text, conn))
{
com.Parameters.Add("#vn", SqlDbType.NVarChar);
com.Parameters.Add("#p_id", SqlDbType.Int).Value = Convert.ToInt32(Session["pa_id"]);
com.Parameters.Add("#note", SqlDbType.NVarChar);
com.Parameters.Add("#Date", SqlDbType.NVarChar).Value = DateTime.Now.ToShortDateString();
conn.Open();
foreach (TreeNode node in TreeView1.CheckedNodes)
{
com.Parameters["#vn"].Value = node.Text;
com.Parameters["#note"].Value = note_vac.Text.Length > 0 ? note_vac.Text : DbNull.Value;
com.ExecuteNonQuery();
}
}
Response.Write("<script>alert('Success!')</script>");
}
catch (Exception ex)
{
Response.Write("Error :" + ex.ToString());
}
}
}
Your original problem was caused by the vn +=... statement that concatenates together all the checked nodes. In this code instead I change that value directly on the Parameter with the value of the current check.
Note also that I have removed the AddWithValue calls that is known to be pretty a bad practice. But this means that you should be precise on the datatype of the values that you pass to your database engine. For example that datetime passed as string is a lot suspicious.
I should also add that having a column named Date is very confusing because there is a DataType with that name and if you want to use that column name it is better to encapsulate it with square brackets to avoid Syntax errors (or rename the column)
I'm loading a table from a database into a CheckedListBox, and now I need to check which items are checked any time the user changes the check status of an item in the first CheckedListBox, and then add the corresponding parts of another table from my database to the second CheckedListBox.
So for example, I have chlbMeal and chlbFood. Inside the chlbMeal there are "Breakfast", "Dinner" and "Lunch". Now when the user selects any of these, I want the corresponding food options to show up in the chlbFood - for example, if "Breakfast" is checked, inside chlbFood we have "Fried eggs", "Eggs and Bacon", etc.
My project is somewhat different but that's the main the idea I want to achieve in this part of it. Here is my code:
private void chlbRadovi_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
if (chlbRadovi.CheckedItems.Count > 0)
{
int[] niz = new int[chlbRadovi.CheckedIndices.Count];
chlbRadovi.CheckedIndices.CopyTo(niz, 0);
foreach (int x in niz)
{
this.tipradovaTableAdapter1.Fill(this.ignaDataSet1.tipradova);
SqlConnection con = new SqlConnection(Konekcija.con);
SqlCommand cmd = new SqlCommand("select IDTR, Naziv from tipradova where IDRad in #IDRad", con);
cmd.Parameters.AddWithValue("#IDRad", chlbRadovi.ValueMember[x]);
SqlDataReader reader;
chlbTipoviRadova.DataSource = ignaDataSet1.tipradova;
chlbTipoviRadova.DisplayMember = "Naziv";
chlbTipoviRadova.ValueMember = "IDTR";
con.Open();
reader = cmd.ExecuteReader();
con.Close();
}
}
else
{
chlbTipoviRadova.DataSource = null;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
How can I do this?
Ok, here we go. First, bind data to your first CheckedListbox:
private string connectionString = "Your connection string";
private void cbListFirst_SetDataSource()
{
// Using block will automatically close connection when it's not used anymore
using (var con = new SqlConnection(connectionString))
{
SqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = #"SELECT Id, Name
FROM dbo.FoodTypes";
try
{
con.Open();
var foodTypes = new List<FoodType>();
using (SqlDataReader reader = cmd.ExecuteReader())
{
// Fill items for first CheckedListBox DataSource
while (reader.Read())
{
foodTypes.Add(new FoodType()
{
Id = (int)reader["Id"],
Name = reader["Name"] as string
});
}
}
// Set first CheckedListBox DataSource
cbListFirst.DataSource = foodTypes;
cbListFirst.DisplayMember = "Name";
cbListFirst.ValueMember = "Id";
}
catch (Exception ex)
{
// Clear DataSource and handle error (should be improved)
cbListFirst.DataSource = null;
MessageBox.Show("Error", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
After you've done that, you should have ParentIds encapsulated inside your FoodType objects inside DataSource of your first CheckedListbox. Now, you shouldn't use SelectedIndexChanged event, but ItemCheck event instead. So every time user checks or unchecks one of the items event will be triggered. Only problem you have now is that inside this event, new CheckValue of clicked item is not yet applied, but it can be handled easy since we have information about new and old value inside EventArgs. Check this out:
private void cbListFirst_ItemCheck(object sender, ItemCheckEventArgs e)
{
// Clear second CheckedListbox DataSource
cbListSecond.DataSource = null;
var ingridients = new List<Ingridient>();
foreach (var item in cbListFirst.CheckedItems)
{
// If item was previously checked, we want to skip it because it's new value is
// unchecked and we shouldn't be adding it's child items to second CheckedListbox
if (cbListFirst.Items.IndexOf(item) != e.Index)
{
var foodType = (FoodType)item;
ingridients.AddRange(GetIngridientsForFoodType(foodType.Id));
}
}
// If item was previously unchecked, it's child items won't be caught in previous loop
// so we want to explicitly include them inside this if-block if new value is checked
if (e.NewValue == CheckState.Checked)
{
var foodType = (FoodType)cbListFirst.Items[e.Index];
ingridients.AddRange(GetIngridientsForFoodType(foodType.Id));
}
// Finally, bind new DataSource
cbListSecond.DataSource = ingridients;
cbListSecond.DisplayMember = "Name";
cbListSecond.ValueMember = "Id";
}
// This method returns list of Ingridients for single FoodType
private List<Ingridient> GetIngridientsForFoodType(int foodTypeId)
{
using (var con = new SqlConnection(connectionString))
{
SqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = #"SELECT Id, Name
FROM dbo.Ingridients
WHERE FoodTypeId = #FoodTypeId";
cmd.Parameters.AddWithValue("#FoodTypeId", foodTypeId);
try
{
con.Open();
var ingridients = new List<Ingridient>();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
ingridients.Add(new Ingridient()
{
Id = (int)reader["Id"],
Name = reader["Name"] as string
});
}
}
return ingridients;
}
catch (Exception ex)
{
// Handle error (should be improved) and return null
MessageBox.Show("Error", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
}
Now your second CheckedListbox should be filled with child values of items from first CheckedListbox.
I am using the following method to load data from a database and fill into my combo box. For example if my database table is as follows:
Table Name: PERSONS
Person_Id Person_Name
1 Jason
2 Carl
The names Jason and Carl will be loaded into the combo box. This works fine. I am now looking to load my combo box only with the column names as in Person_ID, Person_Name. Is there a way I could modify my current method to do that? Please advice.
private void fillCombo(string query, string name, ComboBox c)
{
MySqlCommand cmdReader;
MySqlDataReader myReader;
try
{
cmdReader = new MySqlCommand(query, conn);
myReader = cmdReader.ExecuteReader();
while (myReader.Read())
{
string temp;
if (name != null)
{
temp = myReader.GetString(name);
if (!c.Items.Contains(temp))
{
c.Items.Add(temp);
}
}
}
myReader.Close();
}
catch (Exception e) { Console.WriteLine("Unable to load data from database"); }
}
public MainWindow()
{
InitializeComponent();
fillCombo("SELECT * FROM PERSONS;", "Person_Name", comboBox1);
}
You can get column names like this:
cmdReader = new MySqlCommand(query, conn);
myReader = cmdReader.ExecuteReader();
for(int index=0; index < reader.FieldCount; index++)
{
c.Items.Add(myReader.GetName(index));
}
You could use the GetSchemaTable() method on the MySqlDataReader:
private void fillCombo(string query, string name, ComboBox c)
{
MySqlCommand cmdReader;
MySqlDataReader myReader;
try
{
cmdReader = new MySqlCommand(query, conn);
myReader = cmdReader.ExecuteReader();
var table = myReader.GetSchemaTable();
foreach (DataColumn column in Table.Columns)
{
string s = column.ColumnName;
if (!c.Items.Contains(s))
{
c.Items.Add(s);
}
}
myReader.Close();
}
catch (Exception e) { Console.WriteLine("Unable to load data from database"); }
}