GridView RowCreated: Invalid attempt to call FieldCount when reader is closed - c#

I am getting this error when the gridview is created with an empty dataset. What I am trying to do is populate a dropdown list in the EmptyDataTemplate. From reading other posts the error is caused by use SqlDataReader bind gridview after SqlConnection obejct closed. However, doesn't row created fire after the gridview row is populated?
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.EmptyDataRow)
{
string connectionString3 = ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString;
DropDownList ddl = (DropDownList)e.Row.FindControl("EOCEmpty");
using (SqlConnection conn3 = new SqlConnection(connectionString3))
{
SqlCommand cmd3 = new SqlCommand("SELECT DISTINCT GLAccountEOC, EOCDescription FROM Acct_GLAccount WHERE CostCenter = #CostCenter Order By EOCDescription", conn3);
cmd3.Parameters.Add("#CostCenter", System.Data.SqlDbType.Int);
cmd3.Parameters["#CostCenter"].Value = "3215";
try
{
conn3.Open();
SqlDataReader cmdreader3 = cmd3.ExecuteReader();
ddl.DataSource = cmdreader3;
ddl.DataValueField = "GLAccountEOC";
ddl.DataTextField = "EOCDescription";
ddl.DataBind();
cmdreader3.Close();
}
finally
{
conn3.Close();
}
}
}
}

The problem seem occur in these lines:
SqlDataReader cmdreader3 = cmd3.ExecuteReader();
ddl.DataSource = cmdreader3;
As you should know, ExecuteReader() method builds SqlDataReader instance (which is forward-only stream), and the connection seem to be closed when trying to access the reader. Consider using SqlDataReader.Read() method to open the reader and advances to first record, or load the contents into in-memory DataSet/DataTable and use it as GridView's data source instead.
Here is an example to load SqlDataReader contents inside DataTable:
var dt = new DataTable();
using (SqlConnection conn3 = new SqlConnection(connectionString3))
{
SqlCommand cmd3 = new SqlCommand("SELECT DISTINCT GLAccountEOC, EOCDescription FROM Acct_GLAccount WHERE CostCenter = #CostCenter Order By EOCDescription", conn3);
cmd3.Parameters.Add("#CostCenter", System.Data.SqlDbType.Int);
cmd3.Parameters["#CostCenter"].Value = "3215";
try
{
conn3.Open();
SqlDataReader cmdreader3 = cmd3.ExecuteReader();
if (cmdreader3.HasRows)
{
dt.Load(cmdreader3);
ddl.DataSource = dt; // use DataTable instead of SqlDataReader
ddl.DataValueField = "GLAccountEOC";
ddl.DataTextField = "EOCDescription";
ddl.DataBind();
}
cmdreader3.Close();
}
finally
{
conn3.Close();
}
}
If the problem still persists, use RowDataBound event handler instead of RowCreated to populate dropdownlist value (see difference between RowCreated & RowDataBound event).
Similar issue:
asp.net Invalid attempt to FieldCount when reader is closed error

Related

Gridview wont add another row on button click, it only adds one row and it updates that row only, how to fix this?

So I have a dropdown list that is being populate from database, the goal is to get the selected values from the dropdown list and put in the gridview. The problem is it only adds one row and only updates that row when I try to select new values from the dropdownlist.
I have tried doing Datarow row = dt.newrow() but not luck
Back End code of button click
string CS = ConfigurationManager.ConnectionStrings["POS_SystemConnectionString2"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("SELECT * FROM Product_Details WHERE Model = '" + ddlModel.SelectedItem.Text + "' AND Price = '" +ddlPrice.SelectedItem.Text + "'", con);
con.Open();
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
sda.Fill(dt);
GridView1.DataSource = dt;
GridView1.DataBind();
}
}
First image when I select values from dropdown list for first time
Second image is when I select new values from dropdown list but it updates previous table and does not add new row in gridview
On Add Button click you are retrieving new data from database and creating new datatable that's why you are loosing the previously selected data.
You need to maintain the previously retrieved data between two clicks on Add button.
You need to create a separate DataTable in aspx.cs and store it in the ViewState and add new row to that table on button click and re-bind it to the GridView.
You need to write following code to achieve this.
//This method will check if the table exist in the ViewState
//If table does not exist in ViewState, a new table will be created and stored in ViewState
private DataTable GetDataTable()
{
DataTable dt = ViewState["SelectedModels"] as DataTable;
if (dt == null)
{
dt = new DataTable();
dt.TableName = "ColorData";
dt.Columns.Add(new DataColumn("Model", typeof(string)));
dt.Columns.Add(new DataColumn("Price", typeof(string)));
ViewState["SelectedModels"] = dt;
}
return dt;
}
//This method will store DataTable to ViewState
private void SaveDataTable(DataTable dataTable)
{
ViewState["SelectedModels"] = dataTable;
}
//This method will get the data from the database, add it to the DataTable
//And it will re-bind the DataTable to the GridView and store the updated DataTable to ViewState
private void AddItemToList(string modelName, string price)
{
string CS = ConfigurationManager.ConnectionStrings["POS_SystemConnectionString2"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
using (SqlCommand cmd =
new SqlCommand("SELECT * FROM Product_Details WHERE Model = #modelName AND Price = #price", con))
{
var modelParameter = new SqlParameter();
modelParameter.ParameterName = "#modelName";
modelParameter.Value = modelName;
cmd.Parameters.Add(modelParameter);
var priceParameter = new SqlParameter();
priceParameter.ParameterName = "#price";
priceParameter.Value = price;
cmd.Parameters.Add(priceParameter);
con.Open();
using (var sqlReader = cmd.ExecuteReader())
{
var dataTable = GetDataTable();
while (sqlReader.Read())
{
var dataRow = dataTable.NewRow();
//Hear I assume that Product_Details table has Model and Price columns
//So that sqlReader["Model"] and sqlReader["Price"] will not have any issue.
dataRow["Model"] = sqlReader["Model"];
dataRow["Price"] = sqlReader["Price"];
dataTable.Rows.Add(dataRow);
}
SaveDataTable(dataTable);
GridView1.DataSource = dataTable;
GridView1.DataBind();
}
}
}
}
And now the Click Event Handler of the button should call the above method as following.
protected void bttnAdd_Click(object sender, EventArgs e)
{
AddItemToList(ddlModel.SelectedItem.Text, ddlPrice.SelectedItem.Text);
}
This should help you resolve your issue.

Set default selected value of ComboBox in DataGridView

My Form consist of a DataGridView and, inside of that, I have one Column as ComboBox.
The ComboBox is getting filled by the database query.
I wanted to display the default value of ComboBox in the DataGridView. I have loaded values in combo box but could not find a way to set a default value for that.
I have added values to a combo box with the following code on the click of the btnLoadCombo button:
private void btnLoadCombo_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["InventoryManagerConnectionString"].ConnectionString);
//Filling ComboBoxes
con.Open();
SqlCommand cmdGetRootCat = new SqlCommand("SELECT * FROM tblProductCategories", con);
SqlDataReader sdaRootCat = cmdGetRootCat.ExecuteReader();
comboBoxCatTest.Items.Clear();
while (sdaRootCat.Read())
{
this.CatCombo.Items.Add(sdaRootCat["Cat_Name"]);
}
//Filling DataGridView
DataTable dt = new DataTable();
dt.Clear();
SqlCommand cmd = new SqlCommand("SELECT Cat_ID, Cat_Name FROM tblProductCategories", con);
SqlDataReader sda = cmd.ExecuteReader();
dt.Load(sda);
dataGridCatList.DataSource = dt;
con.Close();
}
I expect results as shown in image 2.
You can use
foreach(DataGridViewRow row in dataGridCatList.Rows)
{
if(row.Cells[3].Value != null) //ignore last row which is empty
{
if( row.Cells[3].Value.Equals(1018) )
row.Cells[0].Value = this.CatCombo.Items[0];
}
//...and so on
}
You are going through every row with the foreach() loop and compare the value of Cat_ParentCat with row.Cells[3].Value.Equals(Cat_ParentCatValue). If a match is found, set the default value of the ComboBox with row.Cells[0].Value = this.CatCombo.Items[yourDefaultValue];

Textbox filter through SQL Datagridview - displaying too many columns (Duplicate Columns)

The search method is here:
private void textBox1_TextChanged(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=DESKTOP-HNR3NJB\\mysql;Initial Catalog=stock;Integrated Security=True");
SqlDataAdapter sda = new SqlDataAdapter("SELECT ProductName FROM [stock].[dbo].[Products]", con);
sda.Fill(dt);
dataGridView1.DataSource = dt;
dt.DefaultView.RowFilter = string.Format("ProductName LIKE '%{0}%'", textBox1.Text);
}
Now that does filter out the results in the table, but it adds columns like the picture below:
Search Results
Load data function (gets called as soon as the form is loaded:
public void LoadData()
{
SqlConnection con = new SqlConnection(#"Data Source=DESKTOP-HNR3NJB\mysql;Initial Catalog=stock;Integrated Security=True");
con.Open();
//reading data from sql
SqlDataAdapter sda = new SqlDataAdapter("SELECT * FROM [stock].[dbo].[Products]", con);
dt = new DataTable();
sda.Fill(dt);
dataGridView1.Rows.Clear();
foreach (DataRow item in dt.Rows)
{
int n = dataGridView1.Rows.Add();
dataGridView1.Rows[n].Cells[0].Value = item["ProductID"].ToString();
dataGridView1.Rows[n].Cells[1].Value = item["ProductName"].ToString();
if ((bool)item["ProductStatus"])
{
dataGridView1.Rows[n].Cells[2].Value = "Active";
}
else
{
dataGridView1.Rows[n].Cells[2].Value = "Inactive";
}
dataGridView1.Rows[n].Cells[3].Value = item["Employee"].ToString();
dataGridView1.Rows[n].Cells[4].Value = item["CPU"].ToString();
dataGridView1.Rows[n].Cells[5].Value = item["RAM"].ToString();
dataGridView1.Rows[n].Cells[6].Value = item["SSD"].ToString();
dataGridView1.Rows[n].Cells[7].Value = item["HDD"].ToString();
dataGridView1.Rows[n].Cells[8].Value = item["WindowsVersion"].ToString();
dataGridView1.Rows[n].Cells[9].Value = item["Description"].ToString();
dataGridView1.Rows[n].Cells[10].Value = item["Type"].ToString();
}
con.Close();
}
Thanks
OK, so you're filling the datagridview elsewhere. You would just need to apply the rowfilter to the view to in the textbox_textchanged event
Where you're populating your current datagridview, ensure that you have your dt instantiated in a wider scope so that the textbox event can access it and then all you should have to do in your textchanged event is the following line:
dt.DefaultView.RowFilter = string.Format("ProductName LIKE '%{0}%'", textBox1.text);
This should then limit the rows to what is currently found. Here's an example of a demo database (you will have to change this to suit your needs)
DataTable dt; //declared outside a method so that multiple methods have access to it object.
private void Form1_Load(object sender, EventArgs e)
{
//Some other area where the datagridview is populated with more information
SqlConnection con = new SqlConnection(#"MyConnectionString");
con.Open();
SqlDataAdapter sda = new SqlDataAdapter("SELECT FirstName, LastName, Address, State FROM Employee", con);
dt = new DataTable();
sda.Fill(dt);
dataGridView1.DataSource = dt;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
//all that should be needed to filter the datagridview to your condition
dt.DefaultView.RowFilter = string.Format("FirstName LIKE '%{0}%'", textBox1.Text);
}
Of course, you really need to switch to using statements so that the objects used are disposed of properly but this is to show more that the basic reason your grid is doing this is that you are applying another datasource into the grid and it doesn't know that you still want all the information you had prior just limited to rows that match your filter.
EDIT - FOR CLARIFICATION
Let's do this. In your loaddata code, remark out the entire for each loop. Add the following lines
datagridview1.columns.clear();
datagridview1.datasoure = dt;
This will hide your pre-existing columns for now without you having to do it manually.
And should show the grid with all the information from the query.
Then in your textchanged event remark all your code and replace it with the line I showed above that uses the DefaultView of the datatable (dt)
That should get you up and running. Once that's done, we can make a change to the query that will allow you to show 'Active'/'InActive' instead of the checkbox for the bit field.

Changes from datagridview not being saved in database

I have this form,when I give it some ID and click the highlighted button,shows me some info in a datagridview taken from a database.
First Form
The code of the button is here:
private void kerkoButton_Click(object sender, EventArgs e)
{
try
{
Konektimi();
string query = "SELECT * FROM Vizita WHERE PacientiId=#pacientiId";
using (SqlCommand command = new SqlCommand(query, conn))
{
command.Parameters.AddWithValue("#pacientiId",pacientiTextBox.Text);
conn.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
DataTable dt = new DataTable();
dt.Load(reader);
forma.vizitaDataGridView.DataSource = dt;
forma.ShowDialog();
}
else
{
MessageBox.Show("Nuk ka të dhëna për këtë pacient");
}
}
conn.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Suposing that we get something from the database we get this:
Second Form,with the results
This is a new form that will be opened after clicking the kerkoButton.
NOW,the problem is this: When I edit a row(or some rows),if I click Save,the changes will not be saved in the database.
What should i do?
Could I make a SaveButton in this datagridform?If yes,how?
Take a look at "how to update row in DataGridView?"
You can access the edited row by using the property CurrentRow of your DataGridView like so:
DataRowView dataRowView = yourDataGridView.CurrentRow.DataBoundItem as DataRowView;
DataRow[] rowsToUpdate = new DataRow[] { dataRowView.Row };
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Vizita", con);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.Update(rowsToUpdate);
Alternatively, if you want to update all edited rows at once, you have to create a flag and set it to true whenever you change a property using the PropertyChanged event.
When clicking "Save" you iterate through all rows and add the edited ones to rowsToUpdate.

Populate dropdownlist inside a gridview

I have a Dropdownlist in a Gridview and i have to show the records associated with every id.And the ID contains more than 10 records so how can i show them??
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
con.Open();
var ddl = (DropDownList)e.Row.FindControl("DropDownList1");
//int CountryId = Convert.ToInt32(e.Row.Cells[0].Text);
SqlCommand cmd = new SqlCommand("select LastName from Profile_Master", con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
con.Close();
ddl.DataSource = ds;
ddl.DataTextField = "LastName";
ddl.DataBind();
}
}
FillSelect(myDropDownList, "--select--", "0", true);
public static void FillSelect(DropDownList DropDown, string SelectItemText, string SelectItemValue, bool includeselectitem)
{
List<PhoneContact> obj_PhoneContactlist = getAll();
if (obj_PhoneContactlist != null && obj_PhoneContactlist.Count > 0)
{
DropDown.DataTextField = "PhoneContactName";
DropDown.DataValueField = "id";
DropDown.DataSource = obj_PhoneContactlist.OrderBy(o => o.PhoneContactName);//linq statement
DropDown.DataBind();
if (includeselectitem)
DropDown.Items.Insert(0, new ListItem(SelectItemText, SelectItemValue));
}
}
public static List<PhoneContact> getAll()
{
obj_PhoneContactlist = new List<PhoneContact>();
string QueryString;
QueryString = System.Configuration.ConfigurationManager.ConnectionStrings["Admin_raghuConnectionString1"].ToString();
obj_SqlConnection = new SqlConnection(QueryString);
obj_SqlCommand = new SqlCommand("spS_GetMyContacts");
obj_SqlCommand.CommandType = CommandType.StoredProcedure;
obj_SqlConnection.Open();
obj_SqlCommand.Connection = obj_SqlConnection;
SqlDataReader obj_result = null;
obj_SqlCommand.CommandText = "spS_GetMyContacts";
obj_result = obj_SqlCommand.ExecuteReader();
//here read the individual objects first and append them to the listobject so this we get all the rows in one list object
using (obj_result)
{
while (obj_result.Read())
{
obj_PhoneContact = new PhoneContact();
obj_PhoneContact.PhoneContactName = Convert.ToString(obj_result["PhoneContactName"]).TrimEnd();
obj_PhoneContact.PhoneContactNumber = Convert.ToInt64(obj_result["PhoneContactNumber"]);
obj_PhoneContact.id = Convert.ToInt64(obj_result["id"]);
obj_PhoneContactlist.Add(obj_PhoneContact);
}
}
return obj_PhoneContactlist;
}
I have done this to get my phonecontacts which are in the data base into dropdown you can change the stored procedures and the values according to your need.
Hope this helps:D
We just ran into this issue where I work. Our way around this problem was to first get the DropDownLists UniqueID. This is basically a Client ID. Inside of that ID is a reference to the row of the GridView that it was selected from. THE ONLY PROBLEM is that it seems to add 2 to the row count. So if you select Row 1's DropdownList, the Unique ID will bring you a reference to the 3rd row. So:
Get the unique ID > Split it however you need to to get the row > use the row number to get the values you need.

Categories

Resources