ASPx GridView row cell counter isn't giving me the right cell - c#

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.

Related

Changing the property of a button in a specific row within a gridview (ASP.NET)

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.

Problem with being able to access certain elements in GridView -> TemplateField -> EditItemTemplate

I have this problem with being able to access certain elements in
GridView -> TemplateField -> EditItemTemplate.
To be more clear, I have textbox and button
(in GridView -> TemplateField -> EditItemTemplate) and only one of them must be visible depending on operation. If I want to edit, button must be visible and if I want to add new user then textbox must be visible. I have added my aspx code for you to check and also C# code-behind. The method in C# code is the one which gets called immidiately when I press "Edit" button which lets to edit account details and permissions for one user. And I want that this method would decide whenever to show textbox or button (I have IsUserInsertMode property for that.)
I would be really grateful for someone who would help me out with this.
<asp:GridView CssClass="grid" ID="gridUsers" runat="server"
AutoGenerateColumns="False" DataKeyNames="id"
DataSourceID="dsrcUserList" GridLines="None"
OnRowCommand="gridUsers_RowCommand" OnDataBound="gridUsers_DataBound"
OnRowUpdating="gridUsers_Updating" OnRowUpdated="gridUsers_Updated" EnableModelValidation="True">
<Columns>
<asp:TemplateField HeaderText="Password" ItemStyle-HorizontalAlign="center" ItemStyle-Width="80px">
<ItemTemplate>
*******
</ItemTemplate>
<EditItemTemplate>
**NEED REFERENCES TO THESE TWO ITEMS IN ORDER TO SET THEIR VISIBILITY**
<asp:Button ID="btnEditPassword" Text="Change" runat="server" CausesValidation="false" OnClick="btnEditPassword_Click" Visible="false"/>
<asp:TextBox ID="txtPassword" runat="server" TextMode="Password" Text='<%# Bind("Password") %>' Width="74px" MaxLength="50" Visible="true" />
</EditItemTemplate>
</asp:TemplateField>
**LOTS OF CHECKBOXES LIKE THOSE**
<asp:CheckBoxField DataField="AccessTowsRelease" HeaderText="TowsRelease" ItemStyle-HorizontalAlign="Center">
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:CheckBoxField>
<asp:CheckBoxField DataField="AccessTowsView" HeaderText="TowsView" ItemStyle-HorizontalAlign="Center">
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:CheckBoxField>
<asp:CheckBoxField DataField="AccessSMS" HeaderText="SMS"
ItemStyle-HorizontalAlign="Center">
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:CheckBoxField>
**MY EDIT BUTTON**
<asp:CommandField ButtonType="Link"
ShowEditButton="true" EditText="Edit"
CancelText="Cancel" UpdateText="Save" />
</Columns>
</asp:GridView>
C# code-behind (I have added some of the stuff that I have tried)
// this is called when I press some button which lets me to edit one user data including password
// but when I want to change password, there should be button which would redirect to different
// window to do the password change procedure.
// And when I want to add new user there should be textbox for password, not the button.
protected void gridUsers_RowCommand(Object sender, GridViewCommandEventArgs e)
{
//Found the way to get the index of current row (represents one row of information which is visible for the client in browser)
int rowIndex = Convert.ToInt32(e.CommandArgument);
//if those would be working I wouldn't be here asking for help
btnEditPassword.Visible = true;
txtPassword.Visible = false;
//Can't cast 'System.Web.UI.WebControls.GridView' to type 'System.Web.UI.WebControls.GridViewRow'
Button btnEditPassword = (Button)((GridViewRow)sender).FindControl("btnEditPassword");
//every attempt to use FindControl gets me null
Button btnEditPassword = (Button)this.gridUsers.Rows[rowIndex].FindControl("btnEditPassword");
// my way to check if it works - if its null then I do can't anything (prints to browser console).
if (btnEditPassword != null) Response.Write("<script>console.log('not null')</script>");
else Response.Write("<script>console.log('null')</script>");
}
The RowCommand-event is raised too early for your case. It's raised before the row is going into edit mode, that's why you don't find the controls of your edit-item-template.
First change your markup to handle the RowDataBound-event:
<asp:GridView ... OnRowDataBound="gridUsers_OnRowDataBound" ...>
Then handle this in the OnRowDataBound-event like this:
protected void gridUsers_OnRowDataBound(object sender, GridViewRowEventArgs e) {
// check if row is in edit state
if ((e.Row.RowState == DataControlRowState.Edit) || (e.Row.RowState == (DataControlRowState.Alternate | DataControlRowState.Edit))) {
// look for the control in the row
Button btnEditPassword = (Button)e.Row.FindControl("btnEditPassword");
}
}

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.

Change URL of Hyperlink in ASP.net gridview itemTemplate dynamically

I am creating a UI that contains an itemTemplate w/in a gridView. inside the ItemTemplate I need a link that will be populated dynamically based on the table values I am binding to the table. In other words, sometimes the link will point to a file on my server, and sometimes it will point to another URL. In essence, I need to be able to check a flag on the table I'm binding to the gridview, and then update each rows itemTemplate based on data in the table for the corresponding row.
So far, I have this markup:
<asp:GridView ID="grdVDocuments"
runat="server"
DataSourceID="sqlDS_wwso"
EnableModelValidation="True"
AutoGenerateColumns="False"
OnRowDataBound="grdVDocuments_RowDataBound"
CssClass="documents_DataTable" AllowPaging="True"
>
<Columns>
<asp:TemplateField HeaderText="Download">
<ItemTemplate>
<a href="/<%# Eval("fileName") %>" target="_blank" id="lnkContent">
<img src="images/orange_download_cropped.png" alt="" border="0"/></a>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="displayName" HeaderText="displayName" SortExpression="displayName" />
<asp:BoundField DataField="fileName" HeaderText="fileName" SortExpression="fileName" />
<asp:BoundField DataField="category" HeaderText="category" SortExpression="category" />
<asp:BoundField DataField="sub_category" HeaderText="Sub-Category" SortExpression="sub_category" />
<asp:BoundField DataField="datePosted" HeaderText="datePosted" SortExpression="datePosted" />
</Columns>
</asp:GridView>
and this code-behind, which is bombing because it can't identify the hyperlink
protected void grdVDocuments_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
HyperLink myHyperLink = e.Row.FindControl("lnkContent") as HyperLink;
myHyperLink.NavigateUrl = "<someOtherURL>";
}
}
The data table has a flag on called isFile which is "bit" dataType. When isFile=1 the URL needs to be ".../", otherwise the URL in the hyperlink of the itemtemplate for each row needs to be set to another field in my table that holds a URL; i.e. "someOtherURL".
Any help is appreciated ;)
Thanks!
You need to use a server control inside the ItemTemplate. So, you need to add runat="server" attribute to the a tag. But then, it is an HtmlAnchor element, and you need to cast as HtmlAnchor instead of Hyperlink like (code-behind):
...
var myHyperLink = e.Row.FindControl("lnkContent") as HtmlAnchor;
myHyperLink.HRef = "<someOtherURL>";
...
Or, you can use <asp:Hyperlink> tag in you aspx markup (instead of html anchor a)

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
}

Categories

Resources