Problem: I'm trying to get a value from a HiddenField object and set it to a string. A value of "foo" is being returned as "foo, foo".
Details:
I have a Nested Gridview, and I'm trying to make the child Gridview editable. I have a method GetChildQuery(string id) which form the sql query to get the data for the Child Gridview, and I have the id bound to a HiddenField object in the HTML as so:
<asp:Gridview ID="gvChild" runat="server" AutoGenerateColumns="false" OnRowEditing = "gvChild_RowEditing">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label runat="server" ID="lblNameChild" Text='<% Eval("Name")%>'></asp:Label>
<asp:HiddenField runat="server" ID="hidIDChild" Value ='<%# Bind("ItemID") %>' />
</ItemTemplate>
<EditItemTemplate>
<asp:Label runat="server" ID="lblNameChildEdit" Text='<% Eval("Name")%>'></asp:Label>
<asp:HiddenField runat="server" ID="hidIDChildEdit" Value ='<%# Bind("ItemID") %>' />
</EditItemTemplate>
</asp:TemplateField>
and I try to access it in the OnRowEditing method like:
protected void gvhild_RowEditing(object sender, GridViewEditEventArgs e) {
Gridview child = sender as Gridview;
string itemID = (child.Rows[e.NewEditIndex].FindControl("hidIDChild") as HiddenField).Value.ToString();
string sql = GetChildQuery(itemID);
child.EditIndex = e.NewEditIndex;
child.DataSource = GetData(sql);
child.DataBind();
}
The id is used in the WHERE clause of my SQL query, and therefore the output is incorrect if the value of the id is incorrect. I don't know I this problem is occurring because of how I bind data to the HiddenField or how I'm calling it from the RowEditing method, or something I'm completely overlooking.
Ref: To make the Nested Gridview, I mainly followed https://www.aspsnippets.com/Articles/Nested-GridView-Example-in-ASPNet-using-C-and-VBNet.aspx and https://www.aspforums.net/Threads/133072/Edit-Update-Delete-in-Nested-Child-GridView-in-ASPNet/ which has the general format for my GetData method as well.
EDIT: Problem subverted by replacing the string itemID instantiation to
string itemID= ((child.Rows[e.NewEditIndex].FindControl("hidIDChild") as HiddenField).Value.Split(','))[0].ToString();
essentially splitting the duplication, accessing the first element, converting that to a string, and setting that equal to y string itemID.
However while this subverts the problem, I would still appreciate anyone's feedback as to why the problem is occurring in the first place. Thanks!
You should look at your rendered html.
When dealing with one gridview there is only ever one row open for editing at a time. And within a row's TemplateField the ItemTemplate and EditTemplate are mutually exclusive, that is to say that, when rendered, you will see only one or the other.
Likewise in the code behind, you will only have access to either the ItemTemplate row or the EditTemplate row depending on which event you process on postback.
The same rules apply to a nested/child Gridview.
So for the case you describe, I assume you are trying to use some ID from the parent to build a query to populate the child. If so I do not believe you need any hidden fields at all. This ID fieldname from the parents query should be set in the DataKeyNames property of the parent gridview.
You should then be able to pass the value to the procedure you are building for the child as
protected void gvhild_RowEditing(object sender, GridViewEditEventArgs e) {
string sql = GetChildQuery(gvParent.SelectedDataKey.Value);
child.DataSource = GetData(sql);
child.DataBind();
// Given that you have just populated the child I do not think the
// following is accurate, but that's for you to decide
child.SetEditRow(e.NewEditIndex);
}
Hope this helps.
Related
i'm gonna keep it short and simple
I'm a software engineering student in the 12th grade and as my final project I have decided to make a website. What the website is about doesn't really matter. The problem is this:
In the picture attached there is a textbox inside a templatefield inside that gridview. I need to get the value that the user writes inside. After you write a value you press Purchase. I've looked at similar questions and none offered a working solution. What happens is the value just disappears. I find the right control with FindControl, but the value gets deleted somehow. How do I know I am at the right control? I went to the client side and added to the asp:TextBox the following:
Text="5"
This works perfectly, so I know it gets to the right control but something makes it disappear. My gridview is being populated by a DataSet that is two datasets combined, and I put the Merge command and the DataSource and DataBind are both in if (!this.IsPostBack). I am completely lost and have no idea what to do, Help is much appreciated.The Picture of the Gridview
All the controls in a GridView are accessible by searching the correct row with FindControl. For that you can send the row number as a CommandArgument and use that in code behind. So first start by using OnCommand instead on OnClick and set the CommandArgument on the aspx page.
<asp:TemplateField>
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Purchase" OnCommand="Button1_Command" CommandArgument='<%# Container.DataItemIndex %>' />
</ItemTemplate>
</asp:TemplateField>
And then in code behind
protected void Button1_Command(object sender, CommandEventArgs e)
{
//get the rownumber from the command argument
int rowIndex = Convert.ToInt32(e.CommandArgument);
//find the textbox in the corrext row with findcontrol
TextBox tb = GridView1.Rows[rowIndex].FindControl("TextBox1") as TextBox;
//get the value from the textbox
try
{
int numberOfTickets = Convert.ToInt32(tb.Text);
}
catch
{
//textbox is empty or not a number
}
}
Environment is an oldish application written in ASP 2.0.
I have this code that displays a DB source into an ASP:repeater:
<asp:Repeater ID="Repeater" runat="server" DataSourceID="DS" OnItemDataBound="On_Repeater_ItemDataBound">
<ItemTemplate>
<asp:CheckBox ID="CB" runat="server" Text='<%# Eval("My_DB_Field") %>'></asp:CheckBox>
</ItemTemplate>
</asp:Repeater>
It works fine.
However, I would like to change the code so that sometimes CB.Text = My_DB_Field (as now) and sometimes it equals an other DB field value.
So I will need to do that into code behind's On_Repeater_ItemDataBound method.
But how can I access the template item DB fields inside this method?
In other words, what is the equivalent of
Text='<%# Eval("My_DB_Field") %>'
in code behind?
I see that what I am looking for can be accessed via:
((System.Data.DataRowView)(e.Item.DataItem)).Row.ItemArray[12]
However, Row.ItemArray is an object[]
So it looks extremely hazardous to hard-code the DB field ID (12).
I'd like to know if there is a dictionary like object so I can access it via the DB field name ("My_DB_Field")?
You can use DataBinder.Eval
string value = DataBinder.Eval(e.Item.DataItem, "My_DB_Field").ToString();
or
DataRowView row = e.Item.DataItem as DataRowView;
string value = row["My_DB_Field"].ToString();
I have a dynamically generated ListView which displays a set of groups, retrieved from a database. The ListView template looks like this:
<asp:ListView ID="lvGroups" runat="server">
<ItemTemplate>
<tr>
<td>
<asp:Label ID="lblGroupName" runat="server" Text='<%# Eval("GroupName") %>' />
</td>
<td>
<asp:LinkButton ID="lnkRemove" runat="server" Text="Remove" OnClick="lnkGroupRemove" OnClientClick="Confirm()" />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
As you can see, there is a value, which is pulled from a database, and then a linkbutton for removing that value. When the linkbutton is clicked, it displays a javascript confirmation message, and if you click Yes on that message, then that specific entry will be removed from the database.
Unfortunately I cannot simply remove the GroupName where the ID = row number, because you can both add and remove groups, so that will quickly become inconsistent.
All I truly need is a way to retrieve the GroupName from the same row as the linkbutton that was clicked, if I can figure out how to do that, then I can easily configure a database query to successfully remove the entry. If you have another solution, however, that is also great.
I'm sad to say that I have no example code for the lnkGroupRemove event, as I simply have no clue where to start on this problem.
Any help on this subject would be much appreciated!
You can use the DataKeyNames attribute:
<asp:ListView ID="lvGroups" runat="server" DataKeyNames="ID,GroupName" >
and get the value this way in code-behind:
protected void lnkGroupRemove(object sender, EventArgs e)
{
ListViewItem item = (sender as LinkButton).NamingContainer as ListViewItem;
int ID = (int)lvGroups.DataKeys[item.DataItemIndex].Values["ID"];
string groupName = (string)lvGroups.DataKeys[item.DataItemIndex].Values["GroupName"];
}
I added an ID value to show that additional fields can be added to DataKeyNames and retrieved from code-behind, including fields that are in the data source but not displayed in the ListView.
I've read multiple sources that say Gridview's do not persist the Gridview.DataSource property on postback. My understanding is that in term's of ASP.NET, a postback is any page load that is not the first pageload (see MSDN).
I've got a situation with 2 very similar gridviews.
GvOne.DataSource is null on postback.
GvTwo.DataSource is NOT null on postback.
The only big difference outside of a few differing columns is GvOne is populated with the Entity Framework and LINQ. GvTwo is populated by a DataTable filled by a SqlDataAdapter.
Further, GvOne and GvTwo have a TemplateField with a TextBox that I use to gather user input. Both use the same code to pull the TextBox.Text on postback:
TextBox tb = (TextBox)GvOne.Rows[i].FindControl("actualTxt");
GvOne properly gathers tb.Text. GvTwo always finds the tb.Text value to be 0.
Basic Gridview code:
<asp:GridView ID="GvOne" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Return">
<ItemTemplate>
<asp:TextBox id="actualTxt" runat="server" Text='0' Width="40px"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
...
</Columns>
</asp:GridView>
<asp:GridView ID="GvTwo" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Order">
<ItemTemplate>
<asp:TextBox id="actualTxt" runat="server" Text='0' Width="40px"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
...
</Columns>
</asp:GridView>
Changing GvTwo to use Entity Framework and LINQ is a potential solution, albeit a major undertaking. Does anyone know what is going on here?
UPDATE (See my comment on Joel Etherton's Answer)
Due to popular demand here is the code to populate the gridview within Page_Load event for GvTwo (GvOne is similar):
ordersGV.DataSource = dataSetObject.Tables["activeParts"];
ordersGV.DataBind();
Searching through the code behind I found no other references to ordersGv.Datasource and no other events that are hooked into associated with the page life cycle.
Gridviews do not persist the datasource across postbacks. If you have a gridview that has a non-null datasource then you must be filling that datasource somewhere in your code. It would be instructive to travel through your event cycle to find where exactly the population of the datasource is occuring on postback.
what does your Page_load code look like?
GridView does not keep DataSource property populated over the postbacks for performance issues
Maybe the second gridview is rebinding the datasource on postback?
I've been working for a long time with GridViews and DetailsViews, but yesterday I've come across a new scenario, which I quite do not understand.
I have a GridView with ImageButton (CommandName="Insert") which will change the mode of the DetailsView to Insert. Afterwards I'll look for a DropDownList inside that DetailsView and add some items dynamically. Works fine, but one first the first time I press that ImageButton. If I click on "Cancel" in the DetailsView and press the ImageButton again, the .FindControl() Method returns null. What life cycle problem am I facing here?
I've created this sample: (To make it run in your Visual Studio, just bind a DataSource to the DetailsView, otherwise it will not be rendered)
Markup:
<asp:GridView ID="gvCategory" runat="server" OnRowCommand="gvCategory_RowCommand">
<Columns>
</Columns>
<EmptyDataTemplate>
<asp:ImageButton ImageUrl="~/images/add.png" ID="ibAdd" runat="server" CommandName="Insert" />
</EmptyDataTemplate>
</asp:GridView>
<asp:DetailsView ID="dvCategory" runat="server" Width="150px" AutoGenerateRows="false"
AutoGenerateInsertButton="True" DataSourceID="LinqDataSource1">
<Fields>
<asp:TemplateField HeaderText="foo">
<InsertItemTemplate>
<asp:DropDownList ID="ddlCategory" runat="server" Width="150"></asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView><asp:LinqDataSource ID="LinqDataSource1" runat="server"
ContextTypeName="WebApplication1.DataClasses1DataContext"
TableName="Categories"></asp:LinqDataSource>
Codebehind:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
this.gvCategory.DataBind();
}
}
protected void gvCategory_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Insert")
{
this.dvCategory.ChangeMode(DetailsViewMode.Insert);
DropDownList _ddlCat = (DropDownList)this.dvCategory.FindControl("ddlCategory");
if (_ddlCat != null)
{
_ddlCat.Items.Clear();
_ddlCat.Items.Add(new ListItem() { Text = "-- empty --", Value = "-1" });
}
}
}
I have also tried using a ItemTemplate, and not a InsertItemTemplate, but this results in the same. After using the ChangeMode-Method the DetailsView.CurrentMode == InsertMode. The only thing I can think of is, that the markup is already generated for the ItemTemplate and changing the Mode to InsertMode can't affect the rendered markup, or something like this.
Does anybody have a solution to this? =)
I think you are on the right track. It's hard to tell without seeing all of the code, but basically any time you change the rendering mode of a row in a repeater-type control you need to rebind it so that it's re-rendered. The fact that FindControl is returning NULL means only one thing: THE CONTROL IS NOT THERE. Which means it was not rendered. You can verify this by looking at the control hierarchy.
So, in your handler for Cancel are you rebinding?