DataTable not retaining added rows - c#

This webpage should flow as such:
Call is made to populateTeamMembers(), which first checks to see if the datatable has no rows (indicating this is the first time the function is called) in case it queries SQL to see who is an existing team member, load that into the datatable sqlTeamMembers
This function also checks if there are any rows in a second datatable, newTeamMembers, and if so it merges the contents of newTeamMembers into sqlTeamMembers. The first time the page loads, the newTeamMembers datatable should always be empty, so no merge is performed.
Finally, now that sqlTeamMembers should contain everybody we need, it binds it to a gridview for display on the web page.
Once it is displayed, there is a dropdownlist under the grid view, with a list of employees that may be added to the team. If the selected index is changed on the ddl, you get a postback and selectedIndexChanged() gets called.
The logic in selectedIndexChanged() is that it queries the selected individuals information into a datatable, warns the user if they are already assigned to a different team, and proceeds to add the employee to the newTeamMembers datatable.
Finally, the populateTeamMembers() is called again, this time there is data in the newTeamMembers datatable, and the merge is performed, before binding to the gridView again.
Due to the nature of the two datatables sqlTeamMembers and newTeamMembers, I have initialized them at the head of my class so they should be accessible to all functions.
It works so far as adding a single new line to sqlTeamMembers, but if I attempt to follow through with a second addition, it is wiping out the first addition. For some reason, newTeamMembers is not retaining the new rows, it only ever contains the most recent addition that selectedIndexChanged() creates.
How can I make newTeamMembers retain the new rows each time selectedIndexChanged() gets called?
public partial class Personnel_Admin_Teams : System.Web.UI.Page
{
SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["WebServerConnectionString"].ConnectionString);
SqlDataReader myreader = default(SqlDataReader);
SqlCommand cmd = new SqlCommand();
string sqlStr;
DataTable sqlTeamMembers = new DataTable();
DataTable newTeamMembers = new DataTable();
...
protected void populateTeamMembers(string TeamID)
{
if (sqlTeamMembers.Rows.Count == 0)
{
/*** Read Team Members with Matching TeamID from Employees Table ***/
cnn.Open();
sqlStr = "SELECT [ID], [LastName]+', '+[FirstName] AS [Name], ISNULL([TeamRole], '') AS [TeamRole] FROM [Employees] WHERE [TeamID] = " + TeamID + ";";
cmd = new SqlCommand(sqlStr, cnn);
sqlTeamMembers.Load(cmd.ExecuteReader());
cnn.Close();
}
if (newTeamMembers.Rows.Count > 0)
{
sqlTeamMembers.Merge(newTeamMembers);
}
gvTeamMembers.DataSource = sqlTeamMembers;
gvTeamMembers.DataBind();
try
{
for (int i = 0; i < gvTeamMembers.Rows.Count; i++)
{
DropDownList ddlTeamRole = gvTeamMembers.Rows[i].Cells[2].FindControl("ddlTeamRole") as DropDownList;
string txtTeamRole = sqlTeamMembers.Rows[i][2].ToString();
txtTeamRole = txtTeamRole.Replace("'", "''");
ddlTeamRole.SelectedValue = txtTeamRole;
}
}
catch (Exception ex)
{
ScriptManager.RegisterClientScriptBlock(Page, typeof(Page), "class", "alert(\"There was an error: " + ex + "\");", true);
}
}
selectedIndexChanged()
{
cnn.Open();
sqlStr = "SELECT [ID], [LastName]+', '+[FirstName] AS [Name], ISNULL([TeamRole], '') AS [TeamRole], ISNULL([TeamID], '0') FROM [Employees] WHERE [ID] = " + ddlAllEmployees.SelectedValue + ";";
cmd = new SqlCommand(sqlStr, cnn);
DataTable addOneMember = new DataTable();
addOneMember.Load(cmd.ExecuteReader());
cnn.Close();
int empTeamID = Convert.ToInt32(addOneMember.Rows[0][3]);
/*** If DT has not been used yet, establish columns ***/
if (newTeamMembers.Columns.Count == 0)
{
newTeamMembers.Columns.Add("ID", typeof(int));
newTeamMembers.Columns.Add("Name", typeof(string));
newTeamMembers.Columns.Add("TeamRole", typeof(string));
}
/*** Remove TeamID Column before merging data back to primary DataTable, perform merge ***/
addOneMember.Columns.RemoveAt(3);
newTeamMembers.Merge(addOneMember);
if (empTeamID != 0)
{
sqlStr = "SELECT [TeamLocation]+' | '+[Department]+' | '+[Type]+' | '+[Team]+' | '+[Shift] AS [Team] FROM [Teams] WHERE [ID] =" + empTeamID + ";";
cmd = new SqlCommand(sqlStr, cnn);
cnn.Open();
myreader = cmd.ExecuteReader();
myreader.Read();
string existingTeam = myreader[0].ToString();
myreader.Close();
cnn.Close();
ScriptManager.RegisterClientScriptBlock(Page, typeof(Page), "class", "alert(\"Warning: User is currently assigned to another team. Employee will be removed from " + existingTeam + " when you save this page.\");", true);
}
}

There are a few things you need to understand.
Web pages are stateless, which means on it's initial pass and subsequent PostBacks you are responsible for saving and restoring state via some persistence medium.
A lot of Server controls can do this for you, but you are opting to manage all of this via DataTables, so you need to implement data persistence. For Database query results, the SqlDataSource control uses the Application Cache. You should use that as well programmatically:
public partial class Personnel_Admin_Teams : System.Web.UI.Page
{
DataTable sqlTeamMembers = null;
DataTable newTeamMembers = null;
protected void Page_Load( object sender, EventArgs e ) {
if( !Page.IsPostBack ){
if( Application.Cache["sqlTeamMembers"] == null )
Application.Cache["sqlTeamMembers"] = new DataTable();
if( Application.Cache["newTeamMembers"] == null )
Application.Cache["newTeamMembers"] = new DataTable();
}
sqlTeamMembers = (DataTable) Application.Cache["sqlTeamMembers"];
newTeamMembers = (DataTable) Application.Cache["newTeamMembers"];
// NOTE: sqlTeamMembers will be null until you make that call to
// populateTeamMembers() so there is no hard and fast rule
// about binding during PageLoad. Bind every time you need
// to reflect changes.
gvTeamMembers.DataSource = sqlTeamMembers;
gvTeamMembers.DataBind();
}
}
Also, just to verify, DataTables are not live, open connections to the database. Changes to DataTables are not automatically written back to your database. You have to make the appropriate INSERT/UPDATE SQL calls.

I think the problem is the placement of this line:
DataTable newTeamMembers = new DataTable();
Since it's at the beginning of the class, it's run each time the page is loaded, meaning everytime you load the page, including when page is a postback. Postback occurs, for example, when the user clicks a button to add a new team member. The page is then posted back to the server with the user entries included.
The behaviour you're looking for is to initialize a new newTeamMembers the first time the page loads, but not when page is a postback. To solve this, declare newTeamMembers at the top of the class, but only initialize it when the page is not a postback, by checking this in the page load event:
public partial class Personnel_Admin_Teams : System.Web.UI.Page
{
DataTable newTeamMembers;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) //page is loaded for the first time
{
newTeamMembers = new DataTable();
}
}
//rest of the code
}

Related

How can I populate a textbox with database values from a combobox?

I am quite new to C# and I am trying to populate fill several textboxes with data from a selected combobox.
I have my main window with the textboxes and comboboxes and a separate class for the connection to the database (I am using XAMPP/PhpMyAdmin).
I managed to fill the comboboxes with the data from the database, but I cannot fill the texboxes from the selected combobox.
I checked other questions and tutorials, but all I managed to achieve is to get the primary key into the text box, but I need different columns from the table, depending on the textbox.
I populated the combobox from the database:
void Completez_Combo_Furnizor()
{
combo_furnizor = DB_Furnizori.Combo_Furnizor();
comboBoxFurnizor.Items.Clear();
comboBoxFurnizor.DataSource = combo_furnizor;
comboBoxFurnizor.ValueMember = "id_furnizor";
comboBoxFurnizor.DisplayMember = "nume";
}
I double clicked on the combobox and wrote the following, but all I can get is the primary key (the first column). In the textbox, I need to get the 7th column (which is a double type.
private void comboBoxFurnizor_SelectedIndexChanged(object sender, EventArgs e)
{
textBoxPret.Text = comboBoxFurnizor.SelectedItem.ToString();
}
And this is from the database class (DB_Furnizori.cs), where I open the connection and have multiple queries for the database.
public static DataTable Combo_Furnizor()
{
conn.Open();
MySqlCommand comboFurnizor = new MySqlCommand("SELECT * from furnizori ORDER BY nume", conn);
MySqlDataAdapter adaptc = new MySqlDataAdapter(comboFurnizor);
DataTable combo_furnizori = new DataTable();
adaptc.Fill(combo_furnizori);
conn.Close();
return combo_furnizori;
}
Please help.
For your SelectedIndexChanged Method :
private void comboBoxFurnizor_SelectedIndexChanged(object sender, EventArgs e)
{
textBoxPret.Text = comboBoxFurnizor.SelectedItem.ToString();
}
You need to make another call to the database and retrieve the data you want using the values/Id (or whatever is the unique identifier) from the combobox. If you're trying to retrieve data on a selected index change you need to reference some type of data source for me I used a DataSet instead of DataTable (makes retrieving values in cells easier):
string StoredProc = "GetItemNotes";
DataSet ds = new DataSet();
SqlCommand cmd = new SqlCommand(StoredProc, conn);
SqlDataAdapter da = new SqlDataAdapter();
cmd.CommandType = CommandType.StoredProcedure;
da.SelectCommand = cmd;
da.Fill(ds);
For populating the data within my Textbox I did this:
Notes.Text = ds.Tables[0].Rows[0]["Notes"].ToString();

NullReferenceException after displaying a table on DataGridView more than once

Edit 2: Turns out it only gives a NullReferenceException the first time. After that, it says that I am not allowed to change the ConnectionString property, even though I think I have closed it in the right places.
Thanks for taking the time to read this.
I am working on a WinForms application that uses an MS Access database, and I am currently having problems with an entry deletion feature I made.
So, I have a DataGridView that switches between 3 tables on a button click, and I have a function that deletes a row on a table that is currently open by clicking a button that is at the end of the row.
When I open my first table, and try to delete a row, it works just fine. However, if I open a different table afterwards and try to delete an entry, or even go back to the first table I opened, I get a NullReferenceException in the deletion function.
Here is the code to display one of the tables in DataGridView.
public DataTable Read()
{
connection.ConnectionString = connectionString;
OpenConnection(); //connection.Open() inside an if statement
dataTable.Clear();
OleDbCommand readStudentCommand = new OleDbCommand("select * from Students", connection); //display the whole list of students
OleDbDataReader reader = readStudentCommand.ExecuteReader();
dataTable.Load(reader);
connection.Close();
return dataTable;
}
Here is the code that deletes an entry
private void MainDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
connection.ConnectionString = connectionString;
if (ConnectionState.Closed == connection.State)
{
connection.Open();
}
var senderGrid = (DataGridView)sender;
if (e.RowIndex >= 0 && senderGrid.Columns[e.ColumnIndex] == MainDataGridView.Columns["Delete"])
{
//this function retrieves the first column value of the deleted row, which has the ID of the entry (works with any table).
DeleteEntry(MainDataGridView.Rows[e.RowIndex].Cells[0].Value.ToString()); //exception thrown here (System.Windows.Forms.DataGridViewCell.Value.get returned null)
MainDataGridView.Rows.RemoveAt(e.RowIndex);
}
and here is DeleteEntry()
private void DeleteEntry(string deletedID)
{
string tableName = null;
string idType = null;
if (studentsDisplayed)
{
tableName = "Students";
idType = "Student ID";
}
else if(booksDisplayed)
{
tableName = "Books";
idType = "BookID";
}
else if(loansDisplayed)
{
tableName = "Loans";
idType = "Loan ID";
}
string deleteCommand = String.Format("DELETE * FROM {0} WHERE [{1}] = {2}", tableName, idType, deletedID);
OleDbCommand deleteEntryCommand = new OleDbCommand(deleteCommand, connection);
deleteEntryCommand.ExecuteNonQuery();
SaveData(); //this method just calls Update() in a dataAdapter of a relevant table
connection.Close();
}
Thank you!
Edit:
As per request, here is the code that switches the table. It simply references the first function and sets the returned dataTable as DataSource.
private void StudentButton_Click(object sender, EventArgs e) //display students
{
try
{
if (!studentsDisplayed)
{
MainDataGridView.DataSource = studentDAL.Read(); //studentDAL is the class that works with the Students table of my DB.
studentsDisplayed = true; //these 3 are to avoid duplicated creation of the same table
booksDisplayed = false;
loansDisplayed = false;
ComboBoxChanger(); //don't mind this, it's for an irrelevant feature
CreateButtons(5);
}
}
catch
{
throw;
}
}
Okay, so turns out the problem was the fact that DeleteEntry(MainDataGridView.Rows[e.RowIndex].Cells[0].Value.ToString()) had a problem with the Cells[0] part. After the first time loading a table, the 0th cell just vanished. So, I rewrote the code so that instead of declaring tableName and idType in DeleteEntry(), they're declared in MainDataGridView_CellContentClick(), and then made the DeleteEntry() accept 3 idType and tableName as parameters, and changed the MainDataGridView.Rows[e.RowIndex].Cells[0].Value.ToString() argument to MainDataGridView.Rows[e.RowIndex].Cells[idType].Value.ToString(). Now it works just fine!

How to store values before postback

I have two dropdownlists, ddlstates and ddlcitys.
The ddlstates has a list of Brazilian states that when clicked, loads the ddlcitys with the cities of that state. Until then, everything works correctly, but when clicking the save button which makes verification of completed fields or not, the ddlcitys back to the first option. How to store the information ddlcitys before the postback?
In code behind, have code that loads the ddlcitys:
protected void ddlstates_TextChanged(object sender, EventArgs e)
{
if (ddlstates.Text != "")
{
List<ListItem> cidades = new List<ListItem>();
SqlConnection conn = new SqlConnection(mytools.stringconection);
SqlDataReader dr = null;
conn.Open();
SqlCommand cmd = new SqlCommand("select ciddesc from cidades where cidestsigla = '" + ddlstates.SelectedValue.ToString() + "' order by 1 asc");
cmd.Connection = conn;
dr = cmd.ExecuteReader();
ddlcitys.Items.Clear();
while (dr.Read())
{
cidades.Add(new ListItem(dr[0].ToString()));
}
dr.Close();
conn.Close();
ddlcitys.DataTextField = "Text";
ddlcitys.DataValueField = "Value";
ddlcitys.DataSource = cidades;
ddlcitys.DataBind();
}
}
Asked long time ago, anyway may the answer help anyone.
On your page load event before bind any of your dropdownlists, make sure that not post back, then on your dropdown select change events , your dropdown values will not re bind so values will not changed.
hint : make sure that your aspx page enable view state (by default enabled) read more.
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
//this will called when your page loaded at first time, so bind your drop down values here.
} else {
//this will called on select change, don't bind your dropdown again, then values will be same (asp.net web forms viewstates will handle read more about viewstate).
}
}

C# Database Displaying Relevant Data in Textboxes

I'm stuck with something and I'd appreciate it if anyone can assist me.
I have a simple MS Access database that's linked to my program. The first thing I did was fill a combobox with one of the fields in my database ("Product Description").
What I'd like to do is when a user selects an item in the combobox, all the other fields in the record be displayed in text boxes.
string sConnection;
sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" +
"Data Source=XYZDatabase.accdb";
OleDbConnection dbConn;
dbConn = new OleDbConnection(sConnection);
dbConn.Open();
cbxProducts.DisplayMember = "Product Description";
dbConn.Close();
I've considered using possibly SQL or a DataReader, though I'm really not sure.
This is the event where I want the textboxes to be filled. I'm literally stuck here.
private void cbxProducts_SelectedIndexChanged(object sender, EventArgs e)
{
txtProductNumber.Text =
txtProductDescription.Text =
txtProductCategory.Text =
txtProductCost.Text =
}
I hope I haven't formatted my question wrong or anything, apologies if I have, this is my first time posting here! Dx
I wonder if your combo box is actually showing data from the DB.
In the first code block after
dbConn.Open()
and before
dbConn.Close()
you should have code like this:
SqlCommand sc = new SqlCommand("select prodid, proddesc from products", conn);
SqlDataReader reader;
reader = sc.ExecuteReader();
DataTable dt = new DataTable();
dt.Columns.Add("prodid", typeof(string));
dt.Columns.Add("proddesc", typeof(string));
dt.Load(reader);
cbxProducts.ValueMember = "prodid";
cbxProducts.DisplayMember = "proddesc";
cbxProducts.DataSource = dt;
And then in the second code block you need to fetch the selected Product ID first.
private void cbxProducts_SelectedIndexChanged(object sender, EventArgs e)
{
string ID = comboBox1.SelectedValue.ToString();
//Based on ID make a sql query and fetch the details of that product from the DB.
//Follow the earlier code block to make DB connection and execute query.
//Then populate the data in individual text boxes by looping through the dr.
//I am leaving this deliberately for you to figure out and I am sure you can.
txtProductNumber.Text =
txtProductDescription.Text =
txtProductCategory.Text =
txtProductCost.Text =
}

Refresh GridView After Data is Deleted From DetailView Using C#

When user select any record from the GridView then my DetailView is updated based on the selection of the GridView. So what I am trying to do is that when I delete anything from the DetailView then I want to refresh the GridView so basically I don’t want to show still the deleted record in the GridView. I have tried to resolve this issue by doing the data bind after my connection and SQL statement but it does not refresh it. One thing to note is that I am using a Accordion pane but both my gridview and the detailview are on the same pane. I am not sure if this is breaking anything. Here is my code:
protected void Refresh_ItemCommand(object sender, DetailsViewCommandEventArgs e)
{
if (e.CommandName.Equals("Delete", StringComparison.CurrentCultureIgnoreCase))
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
SqlDataAdapter da = new SqlDataAdapter("select ID, Name, Address from dbo.MyTable", con);
DataTable dt = new DataTable();
da.Fill(dt);
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
}
You may use the event of the Data view called "ItemDeleted" as follows:
DetailViewName_ItemDeleted(object sender,
DetailsViewDeletedEventArgs e)
{
// Refresh the GridView control after a new record is updated
// in the DetailsView control.
GridViewName.DataBind();
}
The above code is from the official MSDN site for detail view control.
The other way (which I prefer) is to handle the data grid during the Page_load procedure so when you press your delete button in the detail view, the page will perform a postback.
So in the procedure Page_load you can call another procedure which fills the data grid. The code might be like this:
if (isPostback)
{
FillGrid();
}
private void FillGrid()
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
SqlDataAdapter da = new SqlDataAdapter("select ID, Name, Address from dbo.MyTable", con);
DataTable dt = new DataTable();
da.Fill(dt);
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
Your code doesn't show anything where the record is actually deleted so it's understandable that when you re-fetch the data still contains everything.
What you need to do is:
Execute a sql statement to delete the record
Re-fetch the data
Rebind the data.
If you follow those 3 steps it will work.
try to use the event that is called in case of pressing the delete key from the DGV
private void DGV_DeleteKeyPressed(object sender, KeyEventArgs e)
{
//enter code here
}

Categories

Resources