GridView Paging Issue - asp.net

I am using a gridview control and performing Paging and Sorting manually.
Here is the method of Paging:
protected void gdvMainList_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
gdvMainList.PageIndex = e.NewPageIndex;
gdvMainList.DataSource = dtConsentReleaseList;
gdvMainList.DataBind();
}
I have a static datatable having a column Id:
dtConsentReleaseList.Columns.Add("Id");
dtConsentReleaseList.Columns.Add("StartDate");
dtConsentReleaseList.Columns.Add("EndDate");
dtConsentReleaseList.Columns.Add("Contact");
I am assigning datakeynames "Id" in my GridView.
And I also have a print button in each row. When I click that button, this code gets executed:
else if (e.CommandName == "New")
{
int selectedIndex = Convert.ToInt32(e.CommandArgument);
int consentReleaseId = Convert.ToInt32(gdvMainList.DataKeys[selectedIndex].Value);
string openReportScript = Utility.OpenReport(ResolveClientUrl("~/Reports/Consumer/ConsentReleaseReport.aspx?Id=" + consentReleaseId + "&ReportTitle=ConsentForRelease"));
ScriptManager.RegisterClientScriptBlock(upConsentRelease, upConsentRelease.GetType(), "Pop up", openReportScript, true);
}
but when I change the page and clicks print button, an exception occurs on this line :
int consentReleaseId = Convert.ToInt32(gdvMainList.DataKeys[selectedIndex].Value);
Exception is:
Index was out of range. Must be non-negative and less than the size of the collection.Parameter name: index
I guess I am doing something wrong in Paging method.
Any help please?

You are trying to get a value out of an array based on an arbitrary ID instead of the actual index. But you don't need to do that at all. You don't need to store your ID in the DataKeys and you don't need to access anything using the index of the item. Just pull your ID out from the CommandArgument.
<asp:ImageButton CommandName="New" CommandArgument='<%# Eval("Id") %>' ID="ibtnPrint" runat="server" ImageUrl="~/App_Themes/Default/images/print.png" />
And then in code-behind:
int consentReleaseId = int.Parse(e.CommandArgument);

My guess is that you bind the gridview in code-behind (possibly page_load event) and it doesn't hold the values on postback.
Also, try to pass the Id as CommandArgument. As far as I can tell, if you have access to Id of the selected record, you don't need grid's row index at all. (GridView passes row's index as CommandArgument by default)

Set the datakeyname right before you set the datasource and bind the gridview.
In addition,
string[] dk = new string[1] {"MyID"};
myGridView.DataKeyNames = dk;
myGridView.DataSource = ds;
myGridView.DataBind();

Related

How to get the values of the DataSource row that was used to compose another asp:GridView field?

The asp:GridView displays the order lines. It contains only two BoundFields: ID (not visible) and html_text_info (visible). The gvOrderLines.DataSource is explicitly assigned by the DataTable that was obtained by calling the SQL stored procedure. Before that,the html_text_info is added to the DataTable, and it is filled by the formatted HTML text where other columns are the source for the information. See the following method (simplified):
private void LoadGridViewOrderLines()
{
// Get the data table with the order-line data for the order.
DataTable dt = DbUtil.GetAppTable(
"usp_order_lines #order_code=#order_code",
new Hashtable { { "order_code", OrderCode } },
commandTimeoutSeconds: 180);
// Add the field html_text_info, and format the info into it.
dt.Columns.Add("html_text_info", typeof(string));
StringBuilder sb = new StringBuilder();
foreach (DataRow row in dt.Rows)
{
sb.Clear();
// Get the values from the row.
string product_code = row.Field<string>("product_code");
string product_name = row.Field<string>("product_name");
double quantity = ... get the quantity and convert to double...;
string unit = row.Field<string>("unit");
double price = ... get the price and convert to double...;
// Format it as an HTML string.
sb.Append($"{product_code}<br />");
sb.Append($"<b>{product_name}</b><br />");
sb.Append($"Quantity: <b>{quantity:f2} {unit}</b><br />");
sb.Append($"Price: <b>{price:f3}</b><br />");
// Set the formatted value to the field.
row["html_text_info"] = sb.ToString();
}
gvOrderLines.DataSource = dt;
gvOrderLines.DataBind();
}
Now, I want to edit/update the order item. So, I need to access the row from the DataTable that is used as the DataSource. I already have the handler that gets correctly the ID into the property UpdateID (because it was named as DataKeyNames="ID" for the grid view; see below). How can I get the source values that form the composed field?
protected void gvOrderLines_RowEditing(object sender, System.Web.UI.WebControls.GridViewEditEventArgs e)
{
UpdateID = (int)gvOrderLines.DataKeys[e.NewEditIndex].Value;
// How to get the source values that form the composed field?
tbCode.Text = ???;
tbName.Text = ???;
tbQuantity.Text = ???;
tbPrice.Text = ???;
gvOrderLines.EditIndex = -1; // cancel
gvOrderLines.DataBind();
}
Is the DataRow for current e.NewEditIndex easily accessible? Or do I have to search on my own in the DataTable (that is the gvOrderLines.DataSource)?
The "data row" source ONLY persists during the data binding operations.
So for example, you have "looping" code after you pull the data to on the fly create those extra columns. I often do that, but I still in most cases would suggest you do that "one row" operation in the gv, and not against the table.
This suggestion is not a "must" do, but it can help. Since then you are free to have other buttons, filters or whatever and JUST toss/pass/send/throw to the gv the data source. (so, each time you pull/create/have/use/filter etc. the data source, you don't have to modify it with that extra column.
Thus this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView gData = e.Row.DataItem as DataRowView; // the data source row!!!!
Label Info = (Label)e.Row.FindControl("lblInfo");
StringBuilder sb = new StringBuilder();
sb.Clear();
// Get the values from the row.
string product_code = gData["product_code"].ToString();
string product_name = gData["product_name"].ToString();
double quantity = ... get the quantity and convert to double...;
string unit = gData["unit"].ToString();
double price = ... get the price and convert to double...;
// Format it as an HTML string.
sb.Append($"{product_code}<br />");
sb.Append($"<b>{product_name}</b><br />");
sb.Append($"Quantity: <b>{quantity:f2} {unit}</b><br />");
sb.Append($"Price: <b>{price:f3}</b><br />");
// Set the formatted value to the field.
Info.Text = sb.ToString();
}
}
however, as I stated/noted, the so-called data source (dataitem) that seems to show all over the place in most GV events, is ONLY persisted during the binding process. Once binding is done, then that data item goes out of scope.
Next up, a button click to edit/get the one row.
I (now) in most cases do NOT bother with the gv event model WHEN most of the code and editing is custom code (code you the developer is writing and wanting control of what the heck is going to occur!!!).
So, just drop in a plane jane button into the grid row, and use a plane jane standard button click event.
Say, like this:
<asp:BoundField DataField="HotelName" HeaderText="Hotel Name" />
<asp:BoundField DataField="Description" HeaderText="Description" ItemStyle-Width="270" />
<asp:TemplateField HeaderText="Edit">
<ItemTemplate>
<asp:Button ID="cmdEdit" runat="server" Text="Edit"
CssClass="btn" OnClick="cmdEdit_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
So, note how I do (did not) bother with using the built in gv events.
So, the button click event - a plane jane one looks like this:
protected void cmdEdit_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
Debug.Print("row index click = " + gRow.RowIndex);
Debug.Print("database PK id = " + PKID);
string strSQL = "SELECT * FROM tblHotelsA WHERE ID = " + PKID;
DataRow rstData = MyRst(strSQL).Rows[0];
.. etc. etc.
From that row click, we get/use/have the database PK id, and thus have to re-pull the data. Note how we NEVER exposed/used/have the database PK id in the gv markup - (for reasons of security). (and I see you ALSO using datakeys - good!!).
So, your edit button click (now a plane jane click event) becomes somthing like this:
protected void cmdEdit_Click(object sender, EventArgs e)
{
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
// How to get the source values that form the composed field?
// answer: we re-pull the database row based on pk
int UpdateID = (int)gvOrderLines.DataKeys[e.NewEditIndex].Value;
string strSQL = "SELECT * FROM MyTable WHERE ID = " + UpdateID;
DataRow OneRow = MyRst(strSQL).Rows[0];
// any display value in gv - you use find control ,or cells[] collection
TextBox txtHotelName = gRow.FindControl("txtHotel") as TextBox;
Just remember that dataitem" is available ONLY during binding, and after the databinding to the gv is done, then it (the dataitem) goes out of scope.
If the value(s) you need are not in the gv "display", then you need to re-pull that one row for such values.
templated controls in gv - use find control
built in, use cells[] collection.
Now, to be fair, I don't much use the built in "edit" function, and I just drop in a simple edit button, and either hide the gv, show the hidden edit "div" I have. Or better yet, pop that div like this:

changing textbox value in code behind does not post back new value

I have a gridview (ASP.NET Web Form project) with 4 columns in it: A CommandField for editing of the rows, and 3 TemplateField>EditItemTemplate>Textbox columns called Total_Plan, Mth_1 and Mth_2. The 3 textbox columns are bound to an SqlDataSource.
Total_Plan is defined as:
<asp:TemplateField HeaderText="Total Plan">
<EditItemTemplate>
<asp:TextBox ID="txbTotalPlan" runat="server" Text='<%# Bind("Total_Plan") %>'>
</asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="idNotUsedButNeeded0" runat="server" Text='<%# Bind("Total_Plan") %>' />
</ItemTemplate>
</asp:TemplateField>
When the user presses Edit, changes the values of Mth_1 or 2, then presses Update, the following code runs:
protected void gridDetail_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
double totPlan =0;
totPlan += double.Parse(((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbMth_1")).Text);
totPlan += double.Parse(((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbMth_2")).Text);
((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbTotalPlan")).Text = totPlan.ToString();
}
The values entered in Mth_1 and Mth_2 are then updated to to SQL, however, the value of Total_Plan does NOT get updated in the database. The line of code
((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbTotalPlan")).Text = totPlan.ToString();
does change the value of the textbox, because when I check ((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbTotalPlan")).Text I can see the NEW value assigned, however this assignment does not seem to be posted back to the server and so upon Updating the SQL table, the old value is preserved (I inspected the e.Command.Parameters[0].Value of the SqlDataSource_Updating event and indeed, the assignment I do programmatically to the textbox is not picked up when doing the Update to SQL.
I tried adding AutoPostBack=true on the txbTotalPlan but this did not work.
Why if I am doing an assignment, server side, to a textbox in a row of a grid, at the time the update is taking place (i.e. not After), the new value is not used for the Update operation? Am I doing this assignment too late? The alternative I thought was to make the assigmment to the Total Plan column in the OnTextChanged event of each Mth_1 and 2, but if I can write the assignment in a central place and only at a time before updating, it seems less messy and less frequent.
I put a stop in the protected void Page_Load(object sender, EventArgs e) event and indeed at the time of making the assigmment to the txbTotalPlan textbox, this method is not called.
Thank you for your help.
I still dont know about why this doesnt work in the grid's RowUpdating event, but the alternative solution I used was to do the assignment by subscribing to the OnUpdating event of the SqlDataSource and assigning as shown below.
protected void SqlData_Updating(object sender, SqlDataSourceCommandEventArgs e)
{
//calculate Total Plan (sum of all mth columns)
double mth_AddedUp = 0;
foreach(System.Data.Common.DbParameter p in e.Command.Parameters)
if(p.ParameterName.Length > 5 &&
p.ParameterName.Substring(0,5)=="#Mth_" &&
p.Value != null)
mth_AddedUp += double.Parse(p.Value.ToString());
// set Plan parameter value
e.Command.Parameters["#Plan"].Value = (mth_AddedUp).ToString();
}
You are updating data using SqlDataSource. You need to update it's parameter rather than the textbox value. Do like this:
protected void gridDetail_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
double mth1=0;
double mth2=0;
mth1 = double.Parse(((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbMth_1")).Text);
mth2 = double.Parse(((TextBox)gridDetail.Rows[e.RowIndex].FindControl("txbMth_2")).Text);
yourDataSource.UpdateParameters["Mth_1"].DefaultValue = mth1;
yourDataSource.UpdateParameters["Mth_2"].DefaultValue = mth2;
yourDataSource.UpdateParameters["Total_Plan"].DefaultValue = mth1 + mth2;
yourDataSource.Update();//yourDataSource is your SqlDataSource
}

How to get the selected row index and that selected row's cell value using delete button

I have use the templete field delete button in gridview, so when I click on any delete button in the gridview to delete a row at that time it will give the deleting row index with that selected row's cells[0] value. How can I do any one have a idea....?
protected void GridView3_RowCommand(object sender, GridViewCommandEventArgs e)
{
int index = Convert.ToInt32(e.CommandArgument);
int grdid = Int32.Parse(GridView3.Rows[index].Cells[0].Text);
IndiesProductDataContext ip = new IndiesProductDataContext();
ip.iLinks.DeleteAllOnSubmit(ip.iLinks.Where(c => c.ProductID == grdid));
ip.SubmitChanges();
}
I am using row command in gridview deleting row geting index but it does not work
You can set CommandArgument property of button
In Aspx:
<asp:Button ID="btnDeleteContact" CommandName="DeleteContact"
CommandArgument="<%# Eval("ContactID") %>" runat="server" >Delete</asp:Button>
In _RowCommand event of GridView:
If e.CommandName = "DeleteContact" Then
Dim objContacts As New CContacts
objContacts.ContactID = e.CommandArgument
objContacts.DomainID = Session("DomainID")
Dim strError As String = objContacts.DelContact()
End If
hope it helps
You can not do it in that way but you will have to use buttons click event to select the ID and then use that ID as perameter for second, different delete query.
i found The perfect solution of this problem...
protected void GridView3_RowCommand(object sender, GridViewCommandEventArgs e)
{
GridViewRow gvr = (GridViewRow)(((Button)e.CommandSource).NamingContainer);
int RowIndex = gvr.RowIndex;
int ci = Int32.Parse(GridView3.Rows[RowIndex].Cells[1].Text);
ProductDataContext ip = new ProductDataContext();
ip.iLinks.DeleteAllOnSubmit(ip.iLinks.Where(c => c.PID == ci));
ip.SubmitChanges();
}
this code worked properly in my module...

datagridview in asp.net retrieve value

i am working on visual stdio 2008 and my database is in sql server 2005
MY table has three columns
1. SenderName
2. RecieverName
3. Message
i have displayed this table in GridView and add a button named as Reply
so my grid view look's some what like this
SenderName|RecieverName| MessAge|REPLY BUTTON
now this what i want to do
when Button is Clicked in My gridView i need to get data of that specific row
i.e Sender's NAme so that i can Reply him/her ?
can any one help????
Here's a sample:
Markup:
<asp:GridView
runat="server"
ID="gvEmails"
OnSelectedIndexChanged="gvEmails_SelectedIndexChanged">
<Columns>
<asp:ButtonField CommandName="Select" ButtonType="Button" Text="Send" />
</Columns>
</asp:GridView>
Code-behind:
protected void Page_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("SenderName");
dt.Columns.Add("ReceiverName");
dt.Columns.Add("Message");
DataRow dr;
dr = dt.NewRow();
dr["SenderName"] = "John Doe";
dr["ReceiverName"] = "Jane Doe";
dr["Message"] = "Hi, Jane.";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["SenderName"] = "Michelle Smith";
dr["ReceiverName"] = "Mike Smith";
dr["Message"] = "Yo, Mike.";
dt.Rows.Add(dr);
gvEmails.DataSource = dt;
gvEmails.DataBind();
}
protected void gvEmails_SelectedIndexChanged(object sender, EventArgs e)
{
GridViewRow row = gvEmails.SelectedRow;
Response.Write("Send email to " + row.Cells[1].Text);
}
there is a selected index changed function in the properties.
Captuer the selected index and get the cell value of that selected index.
Then continue whtever u want.
There are many different ways of doing this. The easiest, if you only need a single value, would be to bind the value to the CommandArgument of your Reply button. Then add an OnClick handler to your button. Then in the OnClick method you can get the name from the CommandArgument.
If you need more than a single value from the row, you will need to do a little more work. You can setup an event handler on the GridView to capture the event of the index changing. This will provide some event arguments that has a NewSelectedIndex. That will tell you what row was selected. Depending on how your data is bound to your GridView, you can access the data again to get the values you need, or you can set the columns to be a DataKey in the GridView and access them that way.

ASP ListView: How do I access the data that is databound to the rows?

I want to access the data that was databound to my list view when handling list view events such as:
protected void List_ItemDataBound(object sender, ListViewItemEventArgs e)
or
protected void List_ItemCommand(object sender, ListViewCommandEventArgs e)
Inside the events, I can not access the data via somthing like Eval("ID")
Currently we are using a very hacky solution:
string id = e.Item.FindControl("lblID").Text;
Where lblID is a hidden control that is populated with data in the aspx file using:
<asp:Label ID="lblID" runat="server" Text='<%# Eval("ID") %>' />
My eyes bleed when I look at this, Is there a better way?
In your event handlers, use the EventArgs object.
e.Item.DataItem
will give you the object you're looking for; you then just need to cast it to the type you need.
This MSDN page has a fuller example.
For the ItemCommand event handler, you may not have this option. In this case, I would use the CommandName and CommandArgument properties of the LinkButton (or whatever you're using). Put your ID as the CommandArgument, you can then pick this up from the event argument object in the event handler.
After a bit of tinkering I found the proper solution:
Data keys need to be added to the list view. Data keys are persistent unlike the data that is databound to a listview. To set the data key just specify the name in the ListView tag:
<asp:ListView ID="MyListview" runat="server" DataKeyNames="ID" ......
Then to access the keys from the event:
protected void MyListView_ItemCommand(object sender, ListViewItemEventArgs e)
{
// Get the item index of the Item
int itemIndex = ((ListViewDataItem)e.Item).DisplayIndex;
// Extract the key and cast it to its data type.
DataKey key = ((ListView)sender).DataKeys[itemIndex];
int myId = (int) key;
// Use ID to delete / modify the item in the SQL database....
}
Just to expand on the ItemDataBoundEvent solution you alluded to, you don't need to go via the ListView's DataKeys to access the data in the ItemDataBoundEvent. Casting e.Item to ListViewDataItem gives you access to the DataItem property which you can then cast to the underlying data type, giving you intellisense access to every underlying data field. Example:-
(ActualDataType)(((ListViewDataItem)e.Item).DataItem)

Resources