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
}
Related
When I click on a row in the gridview I want to change the Visible property of just the button in that row to true (I want all the other buttons in the other rows to remain invisible). With my current code when I click on a row, the button in every row becomes visible. I'm trying to make this change within the c# code behind the aspx.
This is the current SelectedIndexChanged method:
protected void gridviewAnnouncement_SelectedIndexChanged(object sender, EventArgs e) {
this.gridviewAnnouncement.Rows[gridviewAnnouncement.SelectedIndex].Cells[1].Text = getHeaderAndBody();
foreach (GridViewRow row in gridviewAnnouncement.Rows)
{
Button btn = row.FindControl("btnInterested") as Button;
btn.Visible = true;
}
}
This is the aspx code for the gridview and button:
<asp:GridView ID="gridviewAnnouncement" runat="server" AutoGenerateColumns="false"
AutoGenerateSelectButton="true" OnSelectedIndexChanged="gridviewAnnouncement_SelectedIndexChanged"
GridLines="Horizontal" BorderStyle="Solid">
<Columns>
<asp:BoundField DataField="Header" HeaderText="Announcement" HtmlEncode="false" />
<asp:TemplateField ShowHeader="false">
<ItemTemplate>
<asp:Button runat="server" ID="btnInterested" Text="Interested"
OnClick="btnInterested_Click" Visible="false"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Here are two screenshots of what currently happens:
Before 'Details' is clicked
After 'Details' is clicked on just one row
The only thing I'm trying to fix is having only the button show up for the selected row, not all of them. Thanks to anyone who can help!
but why do you use a foreach when you only want to operate on the ONE row?
And I assume that you can't just click anywhere on that row, but are clicking on the details button, since your selected index would not trigger, then right?
so,
GridViewRow gvR = GridView1.SelectedRow;
Button btn = gvR.FindControl("btnInterested") as Button;
btn.Visible = true;
So, you are using a foreach - and that will operate on all rows in the grid.
You can get the current row in SelectedIndexChange as per above, and then operate ONLY on the one row.
EDIT
The cell count on e.Row.Cells.Count is indicating 6 cells instead of the 3 that I've defined. Again, wtf?
Original Question
I've done this many times before but now I'm getting weird errors...
So what I'm trying to do is set up a javascript confirmation box to appear and prompt the user to confirm deleting a record. I do this by cast the Delete CommandField control to a LinkButton and modifying the OnClientClick value:
<asp:GridView runat="server" ID="gvItems"
DataKeyNames="Id"
PageSize="20"
OnRowDataBound="gvItems_RowDataBound"
OnRowDeleting="gvItems_RowDeleting"
OnSorting="gvItems_Sorting"
OnPageIndexChanging="gvItems_PageIndexChanging"
OnSelectedIndexChanging="gvItems_SelectedIndexChanging">
<Columns>
<asp:BoundField DataField="Description" HeaderText="Feature Description" SortExpression="Description" />
<asp:CommandField ShowSelectButton="true" SelectText="<i class='glyphicon glyphicon-pencil'></i>" />
<asp:CommandField ShowDeleteButton="true" DeleteText="<i class='glyphicon glyphicon-trash'></i>" />
</Columns>
</asp:GridView>
Here's the C# code:
protected void gvItems_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType != DataControlRowType.DataRow) return;
var btn = e.Row.Cells[e.Row.Cells.Count - 1].Controls[0] as LinkButton;
btn.OnClientClick = "return confirm('Are you sure you want to delete this feature?');";
}
Using e.Row.Cells.Count - 1 to target the cell, the control it returns is a Checkbox (???????)
If I change this to use e.Row.Cells.Count instead, now I'm getting an ArgumentOutOfRangeException.
What's frustrating about this is that it works in another project as written above so I really haven't got the first clue where these errors are coming from.
Any ideas?
Note that AutoGenerateColumns is not specified on the GridView.
The problem here is that the GridView defaults this property to true if you don't specify a value for.
This caused the column count to double so either way I tried I was always targeting the wrong column.
Solution was to explicitly set AutoGenerateColumns to false.
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.
I'm new to ASP.NET and don't really understand how to display a table with database data. The data is loaded in an object, where I have a list of objects.
Now I want to show a table with some columns, an image (path created from two fields in the object), an link (passing one field of the object), a textarea (id from object, value from object), a radiobutton (id from object).
How should I do this. I have tried binding a datagrid to the list of objects, and it works. But I don't want to show all data members, and I don't know how to create correct headers and the image and form controls.
ImageDataGridView.DataSource = tradeObj.Images;
ImageDataGridView.DataBind();
To stop the automatic creation of a column for each column in the data source, set:
ImageDataGridView.AutoGenerateColumns = false
Then you need to define a column for each column in the data source that you want to display - depending on the column you may want a bound column (you can control the format) or something more involved.
See http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.datagrid.columns.aspx for a sample and the different column types available
You can spefify your column and header using boundfield
<asp:boundfield datafield="yourColumn"
headertext="theHeaderText"/>
And you need to turn off:
ImageDataGridView.AutoGenerateColumns = false
To format values look at this link at msdn.
You need to use template field to customize your datagrid as per below
<asp:GridView ID="GridView1" runat="server">
<Columns>
<asp:TemplateField ShowHeader="True">
<ItemTemplate>
<asp:Image runat="server" ID="Image1"
ImageUrl="Enabled.gif" />
</ItemTemplate>
</asp:TemplateField>
......
......
</Columns>
</asp:GridView>
See this msdn link to work with template field in datagrid
Update
For iterate through every row of gridview you need to handle Rowdatabound event as per below.
protected void gridView_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
Image imgCtrl = (Image) e.Row.FindControl("imgCtrl");
imgCtrl.ImageUrl = "you can apply any format of url here";
}
}
for more information of Rowdatabound event visit this link
Just set autogenerate to false on your datagrid/gridview control. You can then create a templateField column & drop a label or textbox control inside itemTemplate & bind them like
<asp:Label ID="lblMyColumn" Text="<%# Bind("YourColumnName") %>' runat="server"></asp:Label>
Alternatively you can also drop a boundField & set its properties
<asp:BoundField DataField="YourColumnName" HeaderText="Your Text" SortExpression="YourColumnName" />
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.