I have a drop down list with the list items created in the code behind. ddlFill(). Pretty simple and does the job. It's populated with the current month and some months ahead. I use this drop down as a selection to fill a Gridview. When this drop down index changes, it changes a hidden field value and a corresponding query to fill the gridview. All this works as it should. Within the gridview I have another dropdown list and a button. Both submit the selected row to a database. That also works fine. The problem is, each time one of these rows submits to the database, it causes a post back and it resets the whole page. It resets the drop down list to the first list item . I.E. I change the drop down list to index 4 for example. Which would be April in this case. If I submit a row to the db, the page refreshes and goes back to index 0 .. January in this case. How do I keep it from resetting this way and maintaining the position I was in when I submitted the row?
I have tried a few different options. I've tried session states. hidden field value changes. Nothing seems to work. It either does not perform the post back therefore never submits to the db or it does the post back, submits correctly, then resets the whole page. Including resetting the hiddenfield value back to 0
/* This is up in Page load. */
if (Session["pageStatus"] != null)
{
if (Session["pageStatus"].ToString() == "Loaded")
{
hf2.Value = "Loaded";
}
}
else
{
hf2.Value = "New";
}
if (Session["selectedMonth"] != null)
{
hf1.Value = Session["selectedMonth"].ToString();
}
if (ViewState["button_was_clicked"] != null)
{
ddlFill();
StyleDDL();
}
lblTestlabel.Text = hf2.Value;
AddAttributes();
ShowMonth();
if (!Page.IsPostBack)
{
btnReviewCurrentMonth_OnClick(sender, e);
ddlFill();
StyleDDL();
}
private void ddlFill()
{
string a, b, c, d, e, f;
a = "0";
b = "1";
c = "2";
d = "3";
e = "4";
f = "5";
DropDownList1.Items.Insert(0, new ListItem(ReturnMonth(a))); // A blank object call and the ReturnMonth Method fill the list items.
DropDownList1.Items.Insert(1, new ListItem(ReturnMonth(b)));
DropDownList1.Items.Insert(2, new ListItem(ReturnMonth(c)));
DropDownList1.Items.Insert(3, new ListItem(ReturnMonth(d)));
DropDownList1.Items.Insert(4, new ListItem(ReturnMonth(e)));
DropDownList1.Items.Insert(5, new ListItem(ReturnMonth(f)));
/* These were for various testing options to get it to maintain the
state */
hf2.Value = "Loaded";
Session["pageStatus"] = "Loaded";
DropDownList1.SelectedIndex = Int32.Parse(hf1.Value);
}
My goal is to maintain the state of the page after the submission to the db occurs.
I found this question difficult to ask because it had so many moving parts. The ddlFill() method wasn't the problem. All it litterally did was fill list items. Those corresponding Selected Index values would change when selected and based on those values I would assign a different value to a hidden field. Which was then used as SqlDataSource control variable and would bring back GridView data based on that control value.
A drop down list within the gridview was used to select and submit the indexed row to a SQL DB. At the end of that code I was refreshing the page. It needed a refresh to properly submit and bring back a fresh Gridview with the previously submitted row now gone. That was the problem. When it refreshed, it changed the hidden field value to 0 and reset everything back. So, here is what I did.
//This method controls the Drop Down List change event. Underwriter change.
protected void DropDownList1_OnSelectedIndexChanged(object sender, EventArgs e)
{
DropDownList ddl = (DropDownList)sender;
GridViewRow row = (GridViewRow)ddl.Parent.Parent;
int idx = row.RowIndex;
GridView1.SelectedIndex = idx;
string Client = GridView1.SelectedRow.Cells[0].Text;//Client Name
string NewUw = ddl.Text.ToString();
int UniqCNT = new Int32();
UniqCNT = Int32.Parse(GridView1.SelectedRow.Cells[1].Text.ToString()); //UniqClient */
string ExpPolicyNums = GridView1.SelectedRow.Cells[2].Text;
int Ub = Int32.Parse(GridView1.SelectedRow.Cells[10].Text);//UniqBroker
DateTime ExperationDate = DateTime.Parse(GridView1.SelectedRow.Cells[6].Text); //ExpDate
string Company = GridView1.SelectedRow.Cells[7].Text; //Company issuer
string Broker = GridView1.SelectedRow.Cells[8].Text; //Broker_Name
string Premium = GridView1.SelectedRow.Cells[3].Text; //Premiums
string TotalPremium = GridView1.SelectedRow.Cells[4].Text; //Total premiums
string Reviewed = "No"; //Updates the DB and shows that it hasn't been reviewed by the Message Creator
//DateCreated gets inserted when record is created
string InsertedBy = Request.LogonUserIdentity.Name.Substring(Request.LogonUserIdentity.Name.LastIndexOf(#"\") + 1);
DateTime dateUpDated = DateTime.Now; //Inserts a dateUpdated record
string query = "INSERT INTO [GTU_Apps].[dbo].[Reviewed_Renewal_Policy] (UniqClient, Client, [Expiring_Policies], Premiums, TotalPremium, UniqBroker, ExpDate, NewUw, Company, Broker_Name, Reviewed, DateUpDated, InsertedBy) " +
"VALUES (#UniqCNT, #Client, #ExpPolicyNums, #Premium, #TotalPremium, #Ub, #ExperationDate, #NewUw, #Company, #Broker, #Reviewed, #dateUpDated, #InsertedBy)";
using (SqlConnection conn = new SqlConnection("Data Source=GTU-BDE01;Initial Catalog=GTU_Apps;Integrated Security=True"))
{
using (SqlCommand comm = new SqlCommand(query, conn))
{
comm.Parameters.AddWithValue("#UniqCNT", UniqCNT);
comm.Parameters.AddWithValue("#Client", Client);
comm.Parameters.AddWithValue("#ExpPolicyNums", ExpPolicyNums);
comm.Parameters.AddWithValue("#Premium", Premium);
comm.Parameters.AddWithValue("#TotalPremium", TotalPremium);
comm.Parameters.AddWithValue("#Ub", Ub);
comm.Parameters.AddWithValue("#ExperationDate", ExperationDate);
comm.Parameters.AddWithValue("#NewUw", NewUw);
comm.Parameters.AddWithValue("#Company", Company);
comm.Parameters.AddWithValue("#Broker", Broker);
comm.Parameters.AddWithValue("#Reviewed", Reviewed);
comm.Parameters.AddWithValue("#dateUpDated", dateUpDated);
comm.Parameters.AddWithValue("#InsertedBy", InsertedBy);
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
}
}
GridView1.DataBind();
GridView1.SelectedIndex = -1;
int index = DropDownList1.SelectedIndex;
ConfirmIndex(index);
//End(sender, e);
}
Using the DataBind worked. It only refreshed the Gridview. Which was really what I needed. I had other items that were refreshing too. That's why those calls below it are there. If someone sees this, hopefully it will help.
Related
I am trying to edit a row from the datagridview. Every row has a button. When I press one of the rows button, a second form opens and show me the information in textboxes about that row and I need to edit what I want.
The problem is that I already wrote the code for editing but I can't add the DataGridViewCellEventArgs in the button function, or I can't use RowIndex to edit a specific row.
Here is the code:
public void btnUpdate_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(#"Data Source=DESKTOP-VUPD668;Initial Catalog=dbApp;Integrated Security=True");
SqlCommand cmd;
cmd = new SqlCommand("UPDATE tableApplication SET Name='" + txtName.Text + "',Package='" + txtPackage.Text + "',Hour='" + txtHour.Text + "',Date='" + txtDate.Text + "',Phone='" + txtPhone.Text + "',Observations='" + txtObservations.Text + "' WHERE ID=" + f1.dgvContactList.Rows[rowIndex].Cells[0].Value.ToString(), conn);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
MessageBox.Show("Edit was saved");
this.Close();
}
and here is the code from the main form with the dgv
public void dgvContactList_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 7)
{
formAddEditContact f2 = new formAddEditContact();
int rowIndex = e.RowIndex;
formContact f1 = new formContact();
f2.lblTitle.Text = "Edit";
f2.btnSave.Visible = false;
f2.btnUpdate.Visible = true;
f2.btnDelete.Visible = true;
f2.txtName.Text = f1.dgvContactList.Rows[rowIndex].Cells[1].Value.ToString();
f2.txtPackage.Text = f1.dgvContactList.Rows[rowIndex].Cells[2].Value.ToString();
f2.txtHour.Text = f1.dgvContactList.Rows[rowIndex].Cells[3].Value.ToString();
f2.txtDate.Text = f1.dgvContactList.Rows[rowIndex].Cells[4].Value.ToString();
f2.txtPhone.Text = f1.dgvContactList.Rows[rowIndex].Cells[5].Value.ToString();
f2.txtObservations.Text = f1.dgvContactList.Rows[rowIndex].Cells[6].Value.ToString();
f2.ShowDialog();
How I can use RowIndex in the button function. How I can add DataGridViewCellEventArgs.
It is seldom a good idea to access the displayed data in the DataGridView directly. You should separate the way that data is displayed from the actual values. This way you can easily change the display, without having to change the code that uses the data.
Apparently your DataGridView shows several properties of a sequence of Contacts. Every Row shows one Contact:
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public DateTime BirthDay {get; set;}
...
}
You have added DataGridViewColumns to your DataGridView. Every DataGridViewColumn shows one property. The name of the property that should be shown is in DataGridViewColumn.DataPropertyName
DataGridViewColumn columnBirthDay = new DataGridViewColumn();
columnBirthDay.DataPropertyName = nameof(Contact.BirthDay);
... // set other properties.
Now all you have to do is, get your data and put them in the DataSource of the DataGridView:
IEnumerable<Contact> contactsToDisplay = ...
this.DataGridViewContacts.DataSource = new BindingList<Contact>(contactsToDisplay);
Now every change that the operator edits, is automatically updated in the DataSource. Every change that your program makes to the data source is automatically displayed.
Programmatically add a contact:
BindingList<Contact> DisplayedContacts => (BindingList<Contact>)this.DataGridViewContacts.DataSource;
private void DisplayContact(Contact contact)
{
this.DisplayedContacts.Add(contact);
}
Access edited contacts, for instance after pressing a button:
private void OnButtonOkClicked(object sender, ...)
{
Collection<Contact> editedContacts = this.DisplayedContacts;
this.ProcessEditedContacts(editedContacts);
}
Every row in your DatagridView has a button. If the operator pressed the button you want to use the Contact that is displayed in that row to do something.
private Contact GetContact(DataGridViewRow row)
{
return (Contact)row.DataBoundItem;
}
private void dgvContactList_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
// in baby steps:
DataGridView dataGridView = (DataGridView)sender;
DataGridViewRow row = dataGridView.Rows[e.RowIndex];
Contact contact = GetContact(row);
EditContact(contact);
}
You can do this in one big statement. Not sure if this improves readability though.
private void EditContact(Contact contact)
{
using (var dlg = new EditContactDlg())
{
dlg.Contact = contact;
... // other dialog properties
var dialogResult = dlg.Show(this);
if (dlgResult == DialogResult.OK)
{
... // process edited contact
}
}
}
Be careful: you attach the original unedited contact to the dialog box. If the operator changes values and presses Cancel, the original contact might still be changed. Better is to attach a Cloned contact to the dialog, and if needed use the properties of the edited cloned contact to update the original contact.
Did you see, that because I didn't do everything in one big procedure, the procedures are much easier to understand. They are much easier to be changed slightly, and they can also be reused easily.
For instance, if you decide not to add a column with buttons, because you don't want to display 50 buttons in your form, you decide to add one button to edit the "currently selected row", code changes will be minimal:
private void OnButtonEditCurrentRow_Clicked(object sender, ...)
{
DataGridViewRow row = this.DataGridViewContacts.CurrentRow;
Contact contact = GetContact(row);
EditContact(contact);
}
This procedure can of course also be reused if you want to add a menu item to edit the current row.
Because you separated the way that Contacts are displayed in the datagridview from the actual contacts, code changes will be small if you decide to display the Contact differently, for instance if you decide not to display the Id of the contact anymore, or if you plan to use a Japanese method of displaying the birthday. Or if you implement ordering of the data. The BindingList always contains the internal contact data.
Similarly, if you want to change your contact. For example separate the firstname and the lastName, but still want to display them in one column, code changes will be very small.
I am implementing a functionality display records in gridview not as per default page numbers but as per the department numbers. e.g.
I have 20 departments within that there are students so I want to show department Identifier i.e. D1,D2,D3... as a page numbers and clicking on that students in that department would be loaded in grid.
I want paging like the attached image, on clicking "..." in page numbers it should take me to next set of page numbers.
How should I go for implementation of such a paging for asp.net gridview?
It will be paging through the records using the “Next” and the “Previous” buttons. The Label control will display our current location in the paged GridView. Let’s first set up some of the variables.
protected int currentPageNumber = 1;
private const int PAGE_SIZE = 10;
The currentPageNumber represents the current page of the GridView, and the PAGE_SIZE is the total number of records displayed on each page. You can also allow the user to adjust the page size using a DropDownList, but that is not covered in this article.
Next, we need to bind the data source to the GridView. Let’s check out the BindData method as a whole, and later I will dissect it so you will have a better idea.
private void BindData()
{
string connectionString = "Server=localhost;" +
"Database=Northwind;Trusted_Connection=true";
SqlConnection myConnection = new SqlConnection(connectionString);
SqlCommand myCommand = new SqlCommand("usp_GetProducts",
myConnection);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.AddWithValue("#startRowIndex",
currentPageNumber);
myCommand.Parameters.AddWithValue("#maximumRows", PAGE_SIZE);
myCommand.Parameters.Add("#totalRows", SqlDbType.Int, 4);
myCommand.Parameters["#totalRows"].Direction =
ParameterDirection.Output;
SqlDataAdapter ad = new SqlDataAdapter(myCommand);
DataSet ds = new DataSet();
ad.Fill(ds);
gvProducts.DataSource = ds;
gvProducts.DataBind();
// get the total rows
double totalRows = (int)myCommand.Parameters["#totalRows"].Value;
lblTotalPages.Text = CalculateTotalPages(totalRows).ToString();
lblCurrentPage.Text = currentPageNumber.ToString();
if (currentPageNumber == 1)
{
Btn_Previous.Enabled = false;
if (Int32.Parse(lblTotalPages.Text) > 0)
{
Btn_Next.Enabled = true;
}
else
Btn_Next.Enabled = false;
}
else
{
Btn_Previous.Enabled = true;
if (currentPageNumber == Int32.Parse(lblTotalPages.Text))
Btn_Next.Enabled = false;
else Btn_Next.Enabled = true;
}
}
Now, let’s take a look at the above code in more detail. I am sending the currentPageNumber and the PAGE_SIZE into the database so I can get the data for the current page. The totalRows variable returns the total number of rows in the table. Once I have totalRows, I calculate the total number of pages that will be used for this GridView. The total number of pages is calculated by using a small helper function:
private int CalculateTotalPages(double totalRows)
{
int totalPages = (int) Math.Ceiling(totalRows / PAGE_SIZE);
return totalPages;
}
At the end of the BindData method, there are some conditional checks which ensure that the Next and Previous buttons are only displayed when applicable.
Attaching the Events to the Buttons
The final thing that is left is to attach the events to the Button controls. Check out the following code in which I created two Button controls.
<asp:Button ID="Btn_Previous" CommandName="Previous"
runat="server" OnCommand="ChangePage"
Text="Previous" />
<asp:Button ID="Btn_Next" runat="server" CommandName="Next"
OnCommand="ChangePage" Text="Next" />
Both the buttons call the ChangePage event which is shown below:
// This method will handle the navigation/ paging index
protected void ChangePage(object sender, CommandEventArgs e)
{
switch (e.CommandName)
{
case "Previous":
currentPageNumber = Int32.Parse(lblCurrentPage.Text) - 1;
break;
case "Next":
currentPageNumber = Int32.Parse(lblCurrentPage.Text) + 1;
break;
}
BindData();
}
The ChangePage event is used to change the page number of the GridView and also to update the Label text by calling the BindData method.
Source
My web form starts out as two TextBoxes, two Buttons, a CheckBoxList (bound to the results of a database query), and an empty DropDownList.
When the user enters a search phrase into the first TextBox and hits enter (or clicks the first Button, "Search"), a GridView appears, populated with rows pulled from the database. When the user hits the Select button on one of the rows, the DropDownList is populated (bound to results of a database query) and enabled (if the query returned results -- if there were no results, it remains disabled). When the second Button ("Save Settings") is clicked, the relevant data is saved to the DB, the GridView's selection is cleared, and the DropDownList is cleared and disabled.
All of the above works. The problem comes from the DropDownList. I can't get the C# code to recognize the changing SelectedIndex; depending on how I shuffle my code around, the index is always either 0 (and the DropDownList is forced to stay on the first item), or -1 (and the list becomes disabled).
DropDownList code:
<asp:DropDownList ID="myList" runat="server" AutoPostBack="True"
DataTextField="MyName" DataValueField="MyID"
Enabled="False" onselectedindexchanged="myList_SelectedIndexChanged" />
C# code:
protected void myGrid_SelectedIndexChanged(object sender, EventArgs e)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
if (myGrid.SelectedIndex >= 0)
{
int id = int.Parse(myGrid.Rows[myGrid.SelectedIndex].Cells[2].Text);
connection.Open();
string query = "..."; // Omitted for brevity; the query is correct
SqlDataSource source = new SqlDataSource(connectionString, query);
source.SelectParameters.Add("Param1", TypeCode.String, id.ToString());
DataTable dt = ((DataView)source.Select(DataSourceSelectArguments.Empty)).Table;
dt.AcceptChanges();
myList.DataSource = dt;
myList.DataBind();
myList.Enabled = myList.Items.Count != 0;
if (!myList.Enabled)
{
myList.Items.Add(new ListItem("No Results", "0"));
}
}
}
}
protected void myList_SelectedIndexChanged(object sender, EventArgs e)
{
// ((DropDownList)sender).SelectedIndex == -1
}
I've read that there are some problems with DropDownList while searching for a solution to my problem, but besides the note to set AutoPostBack="True", none of the other situations I've found have helped.
One common reason on why the DropDownList loses its SelectedIndex value is, that during the postback is binded again with data. Do you populate data to the DropDownList somewhere else in your code? Maybe there is something else that causes the SelectedIndex event of the GridView to fire again?
Another thought is that changing the Enabled status of the DropDownList might cause this behavior. Try your code without disabling the DropDownList, and see if something changes.
I am using asp command field type button for editing gridview row.
A row is suppose to be not showing in the grid whenever user update a record. The ShowAllClasses() method is suppose to fetch records from sql db excluding recently updated row.
Now behavior of "Update" Command Field Button.
Localhost:
As I click on the button it hides the row (means binding is done again). In this case record gets updated once. (as required)
On deployed application:
Upon user click the row doesn't hide and user is able to click many times on the button (as if function is not working). But as user stops clicking button, after a minor delay, editing mode of gridview goes away and binding after update is fired.
Bad thing is that the sql table updates each time button is clicked.
Help me how I can fix this.
Here is code I am using
Markup of command field inside GridView1:
<asp:CommandField ButtonType="Button" HeaderText="Edit/Update" ShowEditButton="True"
ShowHeader="True" UpdateText="Set Class" ItemStyle-HorizontalAlign="Center" />
GridView RowUpdating Event:
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
lblError.Text = string.Empty;
int iTutorID = Convert.ToInt32(Session["EID"]);
DropDownList ddlTime = GridView1.Rows[e.RowIndex].FindControl("ddlClassTime") as DropDownList;
string sClassTime = ddlTime.SelectedItem.Text;
HiddenField hf = GridView1.Rows[e.RowIndex].FindControl("sIdHidden") as HiddenField;
int iSID = Convert.ToInt32(hf.Value);
hf = GridView1.Rows[e.RowIndex].FindControl("cIdHidden") as HiddenField;
int iCID = Convert.ToInt32(hf.Value);
hf = GridView1.Rows[e.RowIndex].FindControl("hfClassTime") as HiddenField;
string sOriginalClassTime = hf.Value.ToString();
if (sOriginalClassTime.Length < 8)
sOriginalClassTime = "0" + sOriginalClassTime;
DropDownList ddlDate = GridView1.Rows[e.RowIndex].FindControl("ddlClassDate") as DropDownList;
DateTime dNewDate = Convert.ToDateTime(ddlDate.SelectedValue);
Label lblLesson = GridView1.Rows[e.RowIndex].FindControl("lblLesson") as Label;
string sLesson = lblLesson.Text;
DateTime dClassDate = Convert.ToDateTime(ddlAdvDay.SelectedValue);
//cond: same date and same time set
if (sOriginalClassTime == sClassTime && dNewDate == dClassDate.Date)
{
//show error
lblError.Text = "Same class date and time cannot be set as Advanced class";
return;
}
lblError.Text = string.Empty;
BLClass blClass = new BLClass();
//1. insert in makeup table today's class
//2. insert in classdetails table
//1. insert in makeup table with today's date
blClass.InsertAdvancedClass(iTutorID, iSID, iCID, dClassDate, dNewDate);
//2. insert in classdetails table
blClass.InsertClassDetails(iTutorID, iSID, iCID, dNewDate, sClassTime, "Advanced", sLesson);
GridView1.EditIndex = -1;
ShowAllClasses();
}
Method for Binding Grid with DataSource:
private void ShowAllClasses()
{
lblError.Text = string.Empty;
int iTutorID = Convert.ToInt32(Session["EID"]);
BLClass blClass = new BLClass();
DateTime dClassDate = Convert.ToDateTime(ddlAdvDay.SelectedValue);
DataTable dtClass = blClass.GetAdvancedClasses(iTutorID, dClassDate.Date);
//temp method for date display format
FixDateFormat(dtClass);
dtClass.AcceptChanges();
GridView1.DataSource = dtClass;
GridView1.DataBind();
}
Disable the update link button at the end of the row updating event. Then in the row editing event set the control to active. This would keep the user from clicking the button twice. Sounds like a bad connection to production server.
this.mybutton.enabled = false:
I am writing a web site in Visual Studio, something like an on-line library. I have a GridView on the first page that presents all of the books available from the data source and some other properties also contained in the data source. The GridView contains check boxes and the user can choose which books he wants to order by checking a box. My question is how can I use the data in the selected rows, the list of books with their properties and show that list on another page, so that the user is able to know which items he has selected?
I tried with a for loop on the FirstPage:
protected void Page_Load(object sender, EventArgs e)
{
List<int> ids = new List<int>();
if (!IsPostBack)
{
}
else
{
for (int i = 0; i < GridView1.Rows.Count; i++)
{
int bookID = (int)GridView1.DataKeys[i][0];
CheckBox cb = (CheckBox)GridView1.Rows[i].FindControl("CheckBox");
if (cb.Checked)
{
ids.Add(bookID);
}
}
Session["Ids"] = ids;
Response.Redirect("SecondPage.aspx");
}
}
and on the SecondPage:
protected void Page_Load(object sender, EventArgs e)
{
DataTable dtBooks = new DataTable("Books");
dtBooks.Columns.Add(new DataColumn("ID", typeof(int)));
if (!IsPostBack)
{
var list = (List<int>)Session["Ids"];
foreach (int id in list)
{
if (Request.QueryString["bookID" + id] != null)
{
DataRow row;
row = dtBooks.NewRow();
row["ID"] = Request.QueryString["bookID" + id];
dtBooks.Rows.Add(row);
}
}
GridView1.DataSource = dtBooks;
GridView1.DataBind();
}
else
{
}
}
but I get no GridView table on the second page. I would be very grateful if anyone notices my mistake and points it out. Hope you can help me.
This is a common issue when setting session variables before a redirect. I think you can work around it by using the overloaded Response.Redirect method:
Response.Redirect("...", false); // false = don't stop execution
See here for more details:
Session variables lost after Response.Redirect
Another option is to store the IDs in a hidden field, and access them with Page.PreviousPage, like this:
HiddenField hidden = (HiddenField)Page.PreviousPage.FindControl("MyHiddenField");
string values = hidden.Value;
Lastly, depending on what the page is doing, you might want to use Server.Transfer here. There are drawbacks to this approach, but there are situations where it's applicable.
In your second page, you are checking for a query string variable before adding a row to dtBooks:
if (Request.QueryString["bookID" + id] != null)
However, you are not passing any query strings when you redirect:
Response.Redirect("SecondPage.aspx");
At a guess, I would think that you originally tried using the query string to pass the IDs, before changing to the session and you haven't updated all of your code.
I am somewhat concerned about your first page code, though. You do realize that you will redirect to the second page whenever a post back occurs? That means that no matter what buttons / controls you have on the first page, if they post back for any reason, you will redirect to the second page.
EDIT AFTER COMMENTS
If you aren't using the query string, then don't use the query string:
foreach (int id in list)
{
DataRow row;
row = dtBooks.NewRow();
row["ID"] = id;
dtBooks.Rows.Add(row);
}