Accessing the data or dataItem used to bind a gridview - c#

I have a gridview for which I programmatically set the datasource and databind it to a collection of objects. For each row that is created I then use different methods in the fields to extract the relevant information from the object like this one:
<asp:TemplateField HeaderText="Aliases">
<ItemTemplate>
<%# ( (MyItem)Container.DataItem).Aliases.ToString() %>
</ItemTemplate>
</asp:TemplateField>
My problem is that in the OnRowDeleting method I would preferably like to be able to access that DataItem using e g MyGridView.Rows[e.RowIndex].DataItem or in other way. But I can’t find how to configure the Gridview to retain the DataItem. Is it possible to access the DataItem used and how would I configure it to do it? If that’s not possible can I access the values that are bind by the methods? Or do I have to go with plan B and rewrite the datasource object collection to a datatable and then use datakeysnames?

MyGridView.Rows[e.RowIndex].DataItem should generally work but I guess that you are probably relying the view-state for retaining grid data on post-backs. In such case, you will get the DataItem property as NULL.
Work-around can be to always re-bind the grid with actual data in each postback early in page life cycle (say page_load).
However, in your case, you can very well use DataKeyNames. Contrary to your belief, you don't need a DataTable for this property to work. For example, if your class has property named ItemId indicating the key for your object then you can use DataKeyNames="ItemId" in the markup and refer it in OnRowDeleting using Keys property of event arguments.

According to MSDN:
"The DataItem property is only available during and after the RowDataBound event of a GridView control."
Therefore, access the DataItem in the RowDataBound event:
Lets say you bind a List(Of Vehicle) to the grid:
Dim vehicles As List(Of Vehicle) = Vehicle.GetAll()
gvVehicles.DataSource = vehicles
gvVehicles.DataBind()
In the RowDataBound event access the DataItem:
Protected Sub gvVehicles_RowDataBound(sender As Object, e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
Dim veh As Vehicle = TryCast(e.Row.DataItem, Vehicle)
If Not veh Is Nothing Then
Dim chkBox As CheckBox = CType(e.Row.FindControl("chkSelect"), CheckBox)
chkBox.Checked = True
End If
End If
End Sub

I'm aware this is very dated question at this point - however, I've just run into a similar issue and none of these answers resolved the issue; so I figured I'd post an alternative solution. In my scenario, the issue was on the OnSelectedIndexChanged event. So, it should theoretically hold true for OnRowDeleting but not necessarily on OnRowDeleted (depending on where exactly in the process the row is deleted).
My solution was to simply add a HiddenField for the data that I didn't want to be visible in the GridView, for example:
<asp:GridView ID="gvTutorGroups" runat="server" AutoGenerateColumns="False" DataSourceID="sqlTutorGroups" DataKeyNames="TTGP_Group_Code" AllowPaging="True" PageSize="8" EmptyDataText="You have no tutor groups to display." Style="margin: 0 auto; width: 870px;" OnRowDataBound="gvTutorGroups_RowDataBound" OnSelectedIndexChanged="gvTutorGroups_SelectedIndexChanged">
<Columns>
<asp:TemplateField >
<ItemTemplate>
<asp:HiddenField runat="server" ID="hfTTGPISN" Value="<%# Eval("TTGP_ISN") %>" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="TTGP_Group_Code" HeaderText="TG Code" />
<asp:BoundField DataField="PRPH_Title" HeaderText="Name" />
<asp:BoundField DataField="TTGP_Start_Date" HeaderText="Start Date" DataFormatString="{0:d}" />
<asp:BoundField DataField="TTGP_End_Date" HeaderText="End Date" DataFormatString="{0:d}" />
</Columns>
</asp:GridView>
Then I just used the FindControl method to access that field, like so:
protected void gvTutorGroups_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (GridViewRow row in gvTutorGroups.Rows)
{
if (row.RowIndex == gvTutorGroups.SelectedIndex)
{
row.CssClass = "rowSelected";
DataRowView dataItem = (DataRowView)row.DataItem;
HiddenField hfTGIsn = (HiddenField)this.Parent.FindControl("hfTGisn");
hfTGIsn.Value = ((HiddenField)row.FindControl("hfTTGPISN")).Value;
}
else
{
row.CssClass = "";
}
}
}
It's not an ideal solution, but it works.

Related

Rowdeleteing event in Gridview

I have been trying to write this simple logic of deleting a row from the gridview and from the SQL database of the selected row, but keep getting a null reference to cell, which has the primary key [ID] in a field.
Here's my html:
<asp:GridView ID="grvInventoryEdit" runat="server" BackColor="White" onrowdeleting="grvInventoryEdit_RowDeleting"
onrowediting="grvInventoryEdit_RowEditing"
onrowupdating="grvInventoryEdit_RowUpdating">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:CommandField ShowDeleteButton="True" /><asp:TemplateField HeaderText="Id">
<ItemTemplate>
<%#Eval("No")%>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox runat="server" ID="txtEditNo" ReadOnly="True" Text='<%#Eval("No")%>'></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>........ </Columns> </asp:GridView>
And my back-end code for rowdeleting event is :
protected void grvInventoryEdit_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
TextBox id = (TextBox)grvInventoryEdit.Rows[e.RowIndex].FindControl("txtEditNo");
Asset asset = db.Assets.Single(a => a.No == Convert.ToInt32(id));
db.Assets.DeleteOnSubmit(asset);
db.SubmitChanges();
binddata();
}
and when the event fires, this is what i am seeing while debugging:
I am not sure why i am getting a null value ,though, there is a value for that cell.
Could you tell me what i am doing wrong ?
Regards,
Might be it is due to the readonly property of textbox, not suer.
If you want to use the image button for edit and delete then use
protected void ibtnDelete_Click(object sender, ImageClickEventArgs e)
{
GridViewRow gvRow = (GridViewRow)((ImageButton)sender).NamingContainer;
Int32 UserId = Convert.ToInt32(gvUsers.DataKeys[gvRow.RowIndex].Value);
// delete and hide the row from grid view
if (DeleteUserByID(UserId))
gvRow.Visible = false;
}
For complete code see
You are passing TextBox object instead instead of Text property of TextBox
Asset asset = db.Assets.Single(x=>x.No == Convert.ToInt32(id.Text));
and your TextBox is also coming null means it's unable to find it in GridView, try like this:
TextBox id = e.Row.FindControl("txtEditNo");
Also see this CodeProject article to understand how to use ItemTemplate and EditItemTemplate
why are you adding a second commandfield instead of just enabling the delete button on the existing one.
if you are using a command field you should be supplying an compatible datasource that provides Delete functionality
if you're "rolling your own" delete functionality then just use a regular Button control and supply a CommandName and CommandArgument, such as CommandName="MyDelete" CommandArgument=<row number> where <row number> is supplied via GridView RowDataBound() event.
Regardless of how you choose to implement Delete you should be placing the key field in the GridView DataKeys Property and not as a field within each row. This will make obtaining the PK far easier than what you are trying to do
I guess, in my HTML, i have only the value that's bound to the item, is being displayed, and there are no textboxes in my <ItemTemplate>, hence, it pulls null value. Whereas, in my <EditItemTemplate> there is a textbox, which can pull the value from the cell.
So I changed my HTML to this:
<asp:TemplateField HeaderText="Id">
<ItemTemplate>`<asp:label runat="server" ID="lblEditNo" ReadOnly="True" Text='<%#Eval("No")%>'></asp:label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox runat="server" ID="txtEditNo" ReadOnly="True" Text='<%#Eval("No")%>'></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
and no change in my codebehind, and this resolved the issue.

Making a GridView footer data bound like a Row data

I have an ASP.NET project created with C# that uses GridViews. The footer row contains a ratio of a total based on the column data over a set value from the data saying how many there should be. The user needs to be able to modify the second value and have the footer update. I am not able to find out how to do this or even if it is possible. I contemplated using another GridView underneath, but ensuring the column lines sync between the two is a nightmare. Any ideas?
Also, when I change the column data (using Edit/Update rows), the total is not updated when I click on Update, but does when I click on Edit again. Can anyone tell me why this is and how to update the total in the footer on Updating?
If you find that your edited fields are refreshed "one click behind" typically it's due to thinking you've rebound your GridView but actually haven't or you've rebound before making the Update.
With Gridview, a typical scenario whenever you place fields in a footer is to calculate it's values during databinding operations of the Page Lifecycle
It goes like this, your .aspx file defines some footer fields, typically some BoundField that has been converted to TemplateFields. Here's a snippet:
<asp:GridView ID="GridView1" runat="server" ShowFooter="true" ...>
<Columns>
<asp:BoundField DataField="Field1" HeaderText="Title" SortExpression="Field1" />
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("Field2") %>' ></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:Label ID="FooterLabel1" runat="server" Text="" ></asp:Label>
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
GridView Events in the code behind: This is generally what you need to do to populate footer fields based on changing Row Values. This is VB but you should be able to convert it easily enough.
// Create a variable at the Page level that will exist for
// the duration of the Page LifeCycle
Private FooterLabel1SubTotal as Double
// Initialize
Private Sub GridView1_DataBinding(sender As Object, e As EventArgs) Handles GridView1.DataBinding
FooterLabel1SubTotal = 0.0
End Sub
// Accumulate
Private Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataRow Then
Dim Field2 as Label = e.Row.FindControl("Field2")
FooterLabel1SubTotal += Convert.ToDouble(Field2.Text)
End If
End Sub
// Populate Footer with formated value
Private Sub GridView1_DataBound(sender As Object, e As EventArgs) Handles GridView1.DataBound
FooterLabel1 = GridView1.FooterRow.FindControl("FooterLabel1")
FooterLabel1.Text = String.Format("{0:F2}", FooterLabel1SubTotal)
End Sub
Now if you use any of the Built in Editing features of the GridView, that will cause a Postback and will cause the GridView to rebind, which should recalculate the footer fields each time.

Get Item from GridViewRow ASP.NET

I have a GridView which I Bind a DataSource to it from a SQL Database, the grid has a column with a checkbox in it so I can "select" a few Item in it (Rows actually). What I want here is to do some updates on the Item in each selected rows, but after some search I can't find how to access the Item in a Row, I though DataItem would work, but it's giving me a Null.
Edit: To make it short, I have a GridView which is built from a DataSource, so basically, each rows represent an Object, when I have a checkbox checked in one of the rows, I want to be able to grab the Object related to that Row, what is the easiest way to achieve that?
The DataBinding on Page_Load:
if (!Page.IsPostBack)
{
gvUnusedAccessories.DataSource = currentContext.Items.OfType<Accessory>().Where(ac => ac.Item_Parent_Id == ((PhoneLine)currentItem).Parent.Item_Id);
gvUnusedAccessories.AutoGenerateColumns = false;
gvUnusedAccessories.DataBind();
}
The event when I press the Update Button, It actually browse the rows and if the row has a checked box it's gonna do the update:
protected void btnAddToMobile_Click(object sender, EventArgs e)
{
foreach (GridViewRow row in gvUnusedAccessories.Rows)
{
if(((CheckBox)row.FindControl("chkSelect")).Checked)
{
((Accessory)row.DataItem).PhoneLine_Id = currentItem.Item_Id;
}
}
}
And here's my GridView in the .aspx :
<asp:GridView ID="gvUnusedAccessories" runat="server">
<Columns>
<asp:CommandField ShowSelectButton="True" />
<asp:TemplateField HeaderText="Select">
<ItemTemplate >
<asp:CheckBox ID="chkSelect" runat="server" AutoPostBack="True"/>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Item_Id" HeaderText="ID" ReadOnly="True"/>
<asp:BoundField DataField="Item_Name" HeaderText="Name" ReadOnly="True"/>
<asp:BoundField DataField="AccessoryModel" HeaderText="Modèle" ReadOnly="True"/>
<asp:BoundField DataField="AccessoryBrand" HeaderText="Marque" ReadOnly="True"/>
</Columns>
</asp:GridView>
Any hint on how to access the Object contained inside a Row?
I know I could actually get the ID and then do a SQL request to my DB, but it seems to be a bit heavy and there must be a better solution.
Unless you're storing the DataSource in ViewState or session once the databinding is done you lose the data source. The DataItem will always be null unless you do this. Because of this I often find myself storing the data source in view state. Of course this means your page size is going to get bigger. The other option as you stated is to requery the data source with some sort of primary key. Depending on your needs I can't say which option is better. I tend to lean towards view state rather than a second DB call since that can be an expensive call to make
Given that you have the AutoPostBack set to true on your asp:checkbox you must update the row as you set the checkbox
So I would suggest you you set event OnCheckedChanged="chkStatus_OnCheckedChanged" on asp:checkbox called chkSelect and then you can only update the selected row and don't have to iterate over the enitre grid.
Here is an example how you can get the row items of the selected checkbox
public void chkStatus_OnCheckedChanged(object sender, EventArgs e)
{
CheckBox chkStatus = (CheckBox)sender;
//now grab the row that you want to update
GridViewRow row = (GridViewRow)chkStatus.NamingContainer;
string cid = row.Cells[1].Text;
bool status = chkStatus.Checked;
//now you can do you sql update here
}

ASP.NET 2.0 Gridview CheckBox column

I have a GridView that displays data on three columns. On the third column I have a CheckBox. I have two things that I need to achieve
CheckBox will be checked based on the column value (1,0)
If it is checked the rest of the two columns should display #### However the data for the two columns should remain in the database.
How can this be achieved?
Can I find the CheckBox on RowDataBound event and check the value and make the CheckBox checked and unchecked? How about making the other columns ####?
NEW Comments:
string str = ((System.Data.DataRowView)(e.Row.DataItem)).Row.ItemArray[2].ToString();
this helps to set the checkbox checked or not.
if checked is true I am trying the following.
((System.Data.DataRowView)(e.Row.DataItem)).Row.ItemArray[0] = "#####";
((System.Data.DataRowView)(e.Row.DataItem)).Row.ItemArray[1] = "#####";
((System.Data.DataRowView)(e.Row.DataItem)).Row.AcceptChanges();
It is displaying the gridview checkbox as checked but the column value is not changed to "####"
You can turn your item columns into TemplateColumns and do the following which will localize your code to the control level and you don't have to worry about all the searching. I pefer to never use the built in column types because there are usually future enhancements that require changing the columns to TemplateColumns anyways. It also gives you a lot of flexibiltiy on useage.
Here is an example:
<asp:GridView ID="grdYourGrid" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="YourField1">
<ItemTemplate>
<asp:Literal runat="server" ID="ltYourField1"
OnDataBinding="ltYourField1_DataBinding" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="YourField2">
<ItemTemplate>
<asp:Literal runat="server" ID="ltYourField2"
OnDataBinding="ltYourField2_DataBinding" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="YourCheckBoxField">
<ItemTemplate>
<asp:CheckBox runat="server" ID="chkYourCheckBoxField"
OnDataBinding="chkYourCheckBoxField_DataBinding" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Then in your codebehind implement each control's OnDataBinding:
protected void ltYourField1_DataBinding(object sender, System.EventArgs e)
{
Literal lt = (Literal)(sender);
lt.Text = (bool)(Eval("YourCheckBoxField")) ?
"##########" : Eval("YourField1");
}
protected void ltYourField2_DataBinding(object sender, System.EventArgs e)
{
Literal lt = (Literal)(sender);
lt.Text = (bool)(Eval("YourCheckBoxField")) ?
"##########" : Eval("YourField2");
}
protected void chkYourCheckBoxField_DataBinding(object sender, System.EventArgs e)
{
CheckBox chk = (CheckBox)(sender);
chk.Checked = (bool)(Eval("YourCheckBoxField"));
}
The advantages to doing it this way is your could replace code easily as it is all isolated and has no 'searching' for expected controls. I very rarely use the RowDataBound event because it makes you have to write specific code to look for the controls and it makes more sense to me to have the code localized to the control. If someone changes something they know there are ONLY affecting that one control instead of everything on the row possibly as a side effect. You could also use the <%# method and do the Evals right in the markup but I personally prefer to never have any code in the aspx markup at all.

ASP.NET CheckBoxField - setting value question

I trying to pass some values that the user selects but I am unable to set the CheckBoxField in my gridview.
Could someone please let me know how to do this?
Here is my code so far...
<asp:GridView ID="GridView1" SkinID="CompacGrid" runat="server" AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1" Width="400px" AllowPaging="True" BackColor="White"
BorderColor="#999999" BorderStyle="None" BorderWidth="1px" CellPadding="3"
GridLines="Vertical">
<RowStyle BackColor="#EEEEEE" ForeColor="Black" />
<Columns>
<asp:CheckBoxField headertext="mm" />
Thank you,
Steve
For some reason, Microsoft omitted the 'Value' property from its specification for CheckBox in ASP.NET, despite it being a completely valid feature of HTML. My guess is that they wanted it to be as similar to the WinForms CheckBox as possible (which also lacks a 'Value' property).
If you want to give your <asp:CheckBox /> or <asp:CheckBoxField /> a 'Value' property you will either have to extend the control yourself and define a custom 'Value' property, or make use of the code-behind to populate the Attributes or InputAttributes collection of the control. See this article for more details.
cbMyCheckBoxField.Attributes.Add("Value", "foo");
or
cbMyCheckBoxField.InputAttributes.Add("Value", "foo");
The main difference between these two approaches is that the former will not include the Value in the generated HTML (only ViewState), while the latter will.
Try setting the DataField attribute based on a boolean condition on the datasource object. It appears you're binding to an ObjectDataSource. Perhaps there's a boolean condition on the class -- say it's IsHired.
Perhaps something like:
<asp:CheckBoxField headertext="mm" DataField="IsHired" />
There's a sample on the MSDN CheckBoxField article.
To add onto Nathan's answer: I would use the RowDataBound event for your GridView. Assuming you bound to a DataTable, with the interesting column being [0].
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
//Check for a data row
if (e.Row.RowType == DataControlRowType.DataRow)
{
//Find the checkbox control by ID and set it.
((CheckBox)e.Row.FindControl("CheckBoxId")).Checked = IsItemChecked(((DataRowView)e.Row.DataItem)[0]);
}
}
The IsItemChecked function can be passed the data from the DataItem to create custom logic that passes back a boolean. Once you have the control, you can also add attributes to it. This is a powerful technique once you master it.

Categories

Resources