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.
Related
I am using two combo boxes in one of my programs. The first combo box contains the products while the second contains the categories. I have a method which loads he categories on the second combo box from the database when ever a new item is selected on the first combo box "products". The first time i run the program and select an item it loads from the database but if i try it again nothing loads. Please help with what might be causing this.
private void load_schemes(object sender, EventArgs e)
{
DataTable subjects = new DataTable();
DBConnect con = new DBConnect();
using (SqlConnection CONN = con.getConnection())
{
try
{
schemename.Items.Clear();
SqlDataAdapter adapter = new SqlDataAdapter();
String schemeType = schemetype.Text;
firstname.Text = schemetype.Text;
String SQL = "";
if (schemeType == "Family Scheme")
{
SQL = "select schemeID,SCHEMENAME from registration.familyMedicalScheme";
}
else if (schemeType == "Insurance Scheme")
{
SQL = "select schemeID,SCHEMENAME from registration.insurancescheme";
}
else if (schemeType == "Company Scheme")
{
SQL = "select schemeID,SCHEMENAME from registration.companymedicalscheme";
}
adapter.SelectCommand = new SqlCommand(
SQL, CONN);
adapter.Fill(subjects);
schemename.DataSource = subjects;
schemename.DisplayMember = "SCHEMENAME";
//schemename.ValueMember = subjects.;
}
catch (Exception ex)
{
// Handle the error
}
finally
{
CONN.Close();
}
}
}
I changed the solution and used Items.Add instead of data binding method and it is now working
adapter.Fill(subjects);
foreach (DataRow da in subjects.Rows)
{
schemename.Items.Add(da[0].ToString());
}
//I've list out the codes below, pls take a look and i'd appreciate if i can get some help. My program which is throwing index out of bound exception at line String studadmin = admin[g];
protected void Button1_Click(object sender, EventArgs e)
{
//retrieve studentdetails using List<String[];
List<String[]> stud = new List<String[]>();
int i = 0;
foreach (GridViewRow row in GridView1.Rows)
{
CheckBox check = (CheckBox)row.FindControl("CheckBox1");
if (check.Checked)
{
String [] studDetail = new String [1];
studDetail[0] = row.Cells[1].Text;
stud.Add(studDetail);
}
i++;
}
int g = 0;
foreach (String[] admin in stud)
{
String studadmin = admin[g];
// here's whr the error are prompted (IndexOutOfBoundsException),
// when it reads the following "admin", the loop just ends here with an error;
try
{
myConnection = db.getConnection();
SqlCommand cmd = new SqlCommand("sp_IPPLOAssign");
cmd.Connection = myConnection;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#AcadYear", lb_AcadYear.Text);
cmd.Parameters.AddWithValue("#AcadPeriod", lb_AcadPeriod.Text);
cmd.Parameters.AddWithValue("#IPPProjID", lb_ProjID.Text);
cmd.Parameters.AddWithValue("#ProjSubID", "0");
cmd.Parameters.AddWithValue("#LOLoginID", ddl_LO.SelectedValue);
cmd.Parameters.AddWithValue("#LoginID", Session["UserID"].ToString());
cmd.Parameters.AddWithValue("#Adminno", studadmin);
myConnection.Open();
cmd.ExecuteNonQuery();
lb_Msg.Text = "Update Success.";
lb_error.Text = "";
}
catch (Exception ex)
{
share.WriteLog(ex.Message, "LOAssign.aspx", "Button1_Click()", "Error in updating LO.");
lb_error.Text = "Update failed.";
lb_Msg.Text = "";
}
finally
{
if (myConnection.State == ConnectionState.Open)
myConnection.Close();
}
g++; //loop the subsequent studadmin and store into database
}
refresh_gvCompany();
refresh_gvCurrent(); //refresh gridview
}
Have you confirmed your parameter names are exactly correct? Should #Adminno be #AdminNo, perhaps?
Every item in your stud is an array of String of size 1.
However, at every iteration you are increasing the index of array:
String studadmin = admin[g];
// ...
g++; // loop the subsequent studadmin and store into database
As the result, you actually try to access stud[0][0], stud[1][1]... the errors occurs here, because stud[1] contains only one item.
You need to remove i and g - they are both useless.
As your array only contains one item, it actually looks like you misunderstand usage of List and array. Probably, you want a List<string> instead of List<string[]>:
protected void Button1_Click(object sender, EventArgs e)
{
List<string> stud = new List<string>();
foreach (GridViewRow row in GridView1.Rows)
{
CheckBox check = (CheckBox)row.FindControl("CheckBox1");
if (check.Checked)
{
stud.Add(row.Cells[1].Text);
}
}
foreach (string studadmin in stud)
{
try
{
myConnection = db.getConnection();
SqlCommand cmd = new SqlCommand("sp_IPPLOAssign");
cmd.Connection = myConnection;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#AcadYear", lb_AcadYear.Text);
cmd.Parameters.AddWithValue("#AcadPeriod", lb_AcadPeriod.Text);
cmd.Parameters.AddWithValue("#IPPProjID", lb_ProjID.Text);
cmd.Parameters.AddWithValue("#ProjSubID", "0");
cmd.Parameters.AddWithValue("#LOLoginID", ddl_LO.SelectedValue);
cmd.Parameters.AddWithValue("#LoginID", Session["UserID"].ToString());
cmd.Parameters.AddWithValue("#Adminno", studadmin);
myConnection.Open();
cmd.ExecuteNonQuery();
lb_Msg.Text = "Update Success.";
lb_error.Text = "";
}
catch (Exception ex)
{
share.WriteLog(ex.Message, "LOAssign.aspx", "Button1_Click()", "Error in updating LO.");
lb_error.Text = "Update failed.";
lb_Msg.Text = "";
}
finally
{
if (myConnection.State == ConnectionState.Open)
myConnection.Close();
}
}
refresh_gvCompany();
refresh_gvCurrent();
}
I tried to get data from SQL Server (2 tables: Famille and Compte) into 2 comboboxes in the Form_Load().
But as you see the result, it works with the 1st combobox, but the 2nd it shows System.Data.SqlClient.SqlDataReader
This is the Code
private void Tresorerie_Load(object sender, EventArgs e)
{
conn = new SqlConnection(connstring);
conn.Open();
String queryCompte = "select NomCom from Compte";
String queryFamille = "select NomFam from Famille";
commCompte = new SqlCommand(queryCompte, conn);
commFamille = new SqlCommand(queryFamille, conn);
try
{
//Compte
commCompte.CommandType = CommandType.Text;
dreaderCompte = commCompte.ExecuteReader();
while (dreaderCompte.Read())
{
queryCompte = dreaderCompte[0].ToString();
TreComBoxCompte.Items.Add(queryCompte);
}
}
catch (Exception)
{
MessageBox.Show("Problem with load Compte");
}
finally
{
dreaderCompte.Close();
}
try
{
//Famille
commFamille.CommandType = CommandType.Text;
dreaderFamille = commFamille.ExecuteReader();
while (dreaderFamille.Read())
{
queryFamille = dreaderFamille[0].ToString();
TreComBoxFamille.Items.Add(dreaderFamille);
}
}
catch (Exception)
{
MessageBox.Show("Problem with load Famille");
}
finally
{
dreaderFamille.Close();
}
conn.Close();
}
For second combobox you are adding your datareader dreaderFamille:
TreComBoxFamille.Items.Add(dreaderFamille);
while you should add queryFamille:
queryFamille = dreaderFamille[0].ToString();
TreComBoxFamille.Items.Add(queryFamille);
If you pay attention to item texts in your ComboBox you will guess the problem and when you look at code, you will see your guess is true.
for 2nd comboxbox, you are using wrong object to add
TreComBoxFamille.Items.Add(queryFamille);
You should do it like this:
while (dreaderFamille.Read())
{
queryFamille = dreaderFamille[0].ToString();
TreComBoxFamille.Items.Add(queryFamille);
}
I have a winform app which works correctly but I want to choose the column names, so I created the new columns.
When I open the application now, I have the blank manually generated columns, and then the autogenerated columns containing the data.
so I did:
dataGridView1.AutoGenerateColumns = false;
(on Form1 load)
and then:
List<Movimenti> source = dbo.RecuperaMovimenti(contoSelezionato);
dataGridView1.DataSource = source;
but I get an error:
object reference not set to an instance of an object.
I post some code which should help you to locate the problem:
When I select an item from my listbox, I want the datagridview to load the data for the selected user:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
DboInfo dbo = new DboInfo();
try
{
contoSelezionato = listBox1.SelectedItem.ToString();
}
catch
{}
lblConto.Text = contoSelezionato;
lblConto.Visible = true;
lblTotale.Text = totale;
lblTotale.Visible = true;
try
{
List<Movimenti> source = dbo.RecuperaMovimenti(contoSelezionato);
dataGridView1.DataSource = source;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
dbo method for getting the data:
public List<Movimenti> RecuperaMovimenti(string contoSelezionato)
{
List<Movimenti> risultato = new List<Movimenti>();
SQLiteConnection m_dbConnection = new SQLiteConnection();
try
{
m_dbConnection = OpenConnection();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
SQLiteDataReader rdr;
string sql = "SELECT id_movimento, data_movimento, descrizione_movimento, importo_movimento FROM movimenti WHERE nome_conto = '"+contoSelezionato+"'";
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
rdr = command.ExecuteReader();
while (rdr.Read())
{
Movimenti MOV = new Movimenti();
MOV.id = (Int64)rdr["id_movimento"];
//MOV.nome_conto = (string)rdr["nome_conto"];
MOV.data = (string)rdr["data_movimento"];
MOV.descrizione = (string)rdr["descrizione_movimento"];
MOV.importo = Convert.ToInt32(rdr["importo_movimento"]);
risultato.Add(MOV);
}
rdr.Close();
m_dbConnection.Close();
return risultato;
}
edit:
that's a picture. My data doesn't go into my columns, it just creates auto generated columns
May be, I think errors exist here:
contoSelezionato = listBox1.SelectedItem.ToString();
Can't get value from listBox.
Or here:
while (rdr.Read())
{
Movimenti MOV = new Movimenti();
MOV.id = (Int64)rdr["id_movimento"];
//MOV.nome_conto = (string)rdr["nome_conto"];
MOV.data = (string)rdr["data_movimento"];
MOV.descrizione = (string)rdr["descrizione_movimento"];
MOV.importo = Convert.ToInt32(rdr["importo_movimento"]);
risultato.Add(MOV);
}
One or more item doesn't contain anything value.
This or change
col.DataPropertyName = "ID"
by
col.DataPropertyName = "id"
Movimenti class has "id" property, not "ID"
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();
}