I have reviewed several answers that all seem to indicate that using the format of [textboxID].Text should allow me to reference the input data when I am in the code behind, but this does not hold true for me. My example is as follows:
/** .aspx markup file **/
'''
<asp:ListView ID="ListViewWorkTypes" runat="server" InsertItemPosition="LastItem" >
<LayoutTemplate>
<table id="TypeRecords" runat="server" >
<tr id="TR1" runat="server" >
<td id="Td1" runat="server" >
<table runat="server" id="TypePlaceholder" >
<tr id="Tr2" runat="server" >
<td id="Td10" hidden="hidden" runat="server">Task Name</td>
<td id="Td20" runat="server">PhaseId</td>
<td id="Td30" runat="server">DeptId</td>
<td id="Td11" runat="server">Initial Value</td>
<td id="Td21" runat="server">Order</td>
<td id="Td23" runat="server">Math Operator</td>
<td id="Td31" runat="server">Option Value 1</td>
<td id="Td41" runat="server">Option Value 2</td>
<td id="Td51" runat="server">Option Value 3</td>
<td id="Td61" runat="server">Option Value 4</td>
<td id="Td71" runat="server">Option Value 5</td>
</tr>
<tr runat="server" id="itemPlaceholder">
</tr>
<tr runat="server">
<td runat="server" colspan="2">
<asp:DataPager ID="DataPager1" runat="server">
<Fields>
<asp:NextPreviousPagerField ButtonType="Link" />
<asp:NumericPagerField />
<asp:NextPreviousPagerField ButtonType="Link" />
</Fields>
</asp:DataPager>
</td>
</tr>
</table>
</td>
</tr>
</table>
</LayoutTemplate>
<InsertItemTemplate>
<tr id="NewWorkTaskRow">
<td id="NewTaskName">
<asp:Label runat="server" ID="TaskNameLabel" Visible="true" AssociatedControlID="TaskNameTextBox" Text="Task Name"/>
<asp:TextBox ID="TaskNameTextBox" runat="server" Visible="true" Text='<%# Bind("Name") %>' /><br />
</td>
<td id="NewPhase">
<asp:Label runat="server" ID="PhaseLabel" Text="Phase" AssociatedControlID="PhaseDropDown"/>
<asp:DropDownList ID="PhaseDropDown" runat="server" DataSourceID="SqlDataPhase" DataTextField="PhaseName" DataValueField="PhaseID" AppendDataBoundItems="true" >
<asp:ListItem Text="Choose Phase" Value="" Selected="False"></asp:ListItem>
</asp:DropDownList>
</td>
<td id="NewFunctionalArea">
<asp:Label runat="server" ID="DeptLabel" Text="Fuctional Area" AssociatedControlID="DeptDropDown" />
<asp:DropDownList ID="DeptDropDown" runat="server" DataSourceID="SqlDataDept" DataTextField="Dept" DataValueField="DeptId" AppendDataBoundItems="True">
<asp:ListItem Text="Choose Functional Area" Value="" Selected="True"></asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:Label runat="server" ID="BaseValueLabel" AssociatedControlID="BaseValueTextBox" Text="Base Value" />
<asp:TextBox ID="BaseValueTextBox" runat="server" Text='<%# Bind("BaseValue") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="OrderLabel" AssociatedControlID="OrderTextBox" Text="Order" />
<asp:TextBox ID="OrderTextBox" runat="server" Text='<%# Bind("ordr") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="MathOperatorLabel" AssociatedControlID="MathOperatorTextBox" Text="Math Operator" />
<asp:TextBox ID="MathOperatorTextBox" runat="server" Text='<%# Bind("MathOperator") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="Opt1Label" AssociatedControlID="Opt1TextBox" Text="Option Value" />
<asp:TextBox ID="Opt1TextBox" runat="server" Text='<%# Bind("Opt1") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="Opt2Label" AssociatedControlID="Opt2TextBox" Text="Option Value" />
<asp:TextBox ID="Opt2TextBox" runat="server" Text='<%# Bind("Opt2") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="Opt3Label" AssociatedControlID="Opt3TextBox" Text="Option Value" />
<asp:TextBox ID="Opt3TextBox" runat="server" Text='<%# Bind("Opt3") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="Opt4Label" AssociatedControlID="Opt4TextBox" Text="Option Value" />
<asp:TextBox ID="Opt4TextBox" runat="server" Text='<%# Bind("Opt4") %>' /><br />
</td>
<td>
<asp:Label runat="server" ID="Opt5Label" AssociatedControlID="Opt5TextBox" Text="Option Value" />
<asp:TextBox ID="Opt5TextBox" runat="server" Text='<%# Bind("Opt5") %>' />
</td>
<td>
<asp:LinkButton ID="InsertButton" runat="server" OnClick="InsertType" Text="Insert" ></asp:LinkButton>
</td>
</tr>
</InsertItemTemplate>
'''
/** Code Behind **/
'''
protected void InsertType(object sender, EventArgs e)
{
string Name = "";
int BaseValue = 0;
bool LOE;
int ordr;
char operation;
int Opt1 = 0;
int Opt2 = 0;
int Opt3 = 0;
int Opt4 = 0;
int Opt5 = 0;
int TypeID = 0;
string strtypecon = WebConfigurationManager.ConnectionStrings["DefaultEstimateConnection"].ConnectionString;
SqlConnection typeconx = new SqlConnection(strtypecon);
SqlCommand TypeCmd = new SqlCommand()
{
Connection = typeconx
};
/*** Attempted with the aforementioned TextBox ID reference ***/
Name = TaskNameTextBox.Text;
/*** Err msg: TaskNameTextBox does not exist in current context ***/
/*** Attempted with Request form ***/
Name = Request.Form["TaskNameTextBox"];
/** Checkpoint at this point of the process indicates a null value for Name **/
ListViewWorkTypes.FindControl("TaskNameTextBox").ToString();
TypeCmd.Parameters.AddWithValue("#Name", Name);
'''
This did not seem different from the other code I had seen on this site that had reportedly worked.
Ok,a few things:
first, if you not using the grids built in edit or features, then I would dump all templates EXCEPT for the ItemTemplate. I find in most cases, I don't need all the additional templates, and I blow them out (delete them).
So, I will often drop in a lv, use the wizard to generate the data and layout. I then delete the datasource on the page and then collapse each edit/alternative/insert/edit templates and DELETE them. blow out (delete) the datasource control placed on your form, and you go from a galactic mess of huge markup into a simple nice clear easy to work with lv.
So, even in your case, it not clear if you actually using the insert template, or that you simply drop in a linkbutton and added a event to that link button.
(BTW: you can drop in a plane jane asp.net button - they often look MUCH nicer).
So, I don't know if you using the insert template, but you do have the button dropped into your insert template, but I don't know if its ALSO in the item template.
As noted:
You have (or might) have item/edit/alternating/insert templates. And given the screen might also have many rows? The when you reference a text box, then which one and which row and from which template would you get?
Answer: massive world wide confusing!!!
So, when using a gridview, listview, repeater, formview, details view?
They all work quite much the same - they are "repeaters" in the sense that the layout you have is used over and over. As a result, you of course cannot use ay:
TaskNameTextBox.Text
Now, as noted, it not at all clear if you actually using the inert template. and as noted, in most cases, I simple dump ALL the templates except for Item template. And in most cases it is less code, less markup, less things to maintain.
Ok, however lets address the linkbutton click event and code.
There are actually about 5 approaches here. and picking which approach to get the row you trying to work on will DEPEND VERY much on what you trying to do.
Probably the most easy way to get this to work?
You can add to the button a CommandName tag. This tag has specials names that can cause the iistview events to fire. However, looking at your listview, it don't look like you have ANY of the wired up.
So, you could use CommandName="select"
This will then cause the row command to fire (but row index has NOT changed yet!!! - it will. However, RowCommand event does pass the row.
but, right after above, the SelectedIndex of the listview will fire and MORE important the index DOES change.
However, there also another way to get the current row, and save you to having to learn the COMPLEX event model of the listview.
What you can do is simply pick up and get the current row you clicked on (the insert link button you have). When we get that row you can THEN pluck out values from that row.
However, to get such values, we can't an odn't use the control names. What you do get is a "object"/thing that represents the one row, and from that you can use FindConrol to pull out the value.
As noted, there would be a gallziooon instances of that control, and even different templates- so becomes impossible as to what control you trying to get.
So, for these "repeater" types of controls, you can't reference thee control directly anymore. you ALWAYS have to get the row instance, and THEN from that row instance pluck out/get/grab the control by use of FindControl.
So, the first part, the hard part and the VALUABLE part is to get that row you clicked on.
As noted, you could have/let/allow the row index changed event fire, and from that event you can then get the current row though ListView2.xxxxxx.
Howevver, from that simple button drop into the itemtemplate? (and again I STONG recommend you only have the itemtempate if you not using the lv features to edit (such as insert template).
So, in the button click event, we can get that all knowing and all valuable row. We just need that row thing, and from that we are home free.
it works like this:
protected void cmdLstSel_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
ListViewDataItem gRow = (ListViewDataItem)btn.Parent.Parent.Parent;
// ListViewDataItem gRow = (ListViewDataItem)GetParentObj(btn.Parent, typeof(ListViewDataItem));
int hID = (int)ListView1.DataKeys[gRow.DisplayIndex]["ID"];
note carful the use of Datakeys. You REALLY but REALLY want to set this if you need and want the PK row ID from the database. This also has the HUGE benefit of you not having to include the database PK id in the lv markup. (this also can be huge in terms of security issues - you don't have to include or expose the database PK id in the markup and thus it is NEVER exposed client side - do keep this in mind).
Ok, so the above gets the current row (ListViewDataItem) by using the parent property of that control clicked on. (.parent = "<td">, and we go up one more, and then one more again. So, note how I used .Parent.Parent.Parent. I even wrote a routine to do this for me, since often due to a extra div or whatever, you might have to .parent up a few more to get that row.
And of course in your case, we would do/use this:
LinkButton btn = (LinkButton)sender);
ListViewDataItem gRow = (ListViewDataItem)btn.Parent.Parent.Parent;
// ListViewDataItem gRow = (ListViewDataItem)GetParentObj(btn.Parent, typeof(ListViewDataItem));
int hID = (int)ListView1.DataKeys[gRow.DisplayIndex]["ID"];
But, ONCE we have that gRow, then you can get the value of the given text box like this:
TextBox MyTaskNameTextBox;
MyTaskNameTextBox = (TextBox)gRow.FindControl("TaskNameTextbox");
// text of box now is MyTaskNameTextBox.Text
As noted, sometimes you have to "guess" how many parents to go up to get that all valued "row". I often guess, compile and run, and the error message actually tells me you can't cast that "td" to ListViewitem, so I put in another one and compile again. But, I even became tired of that, so I wrote this routine to get the row for me:
public Control GetParentObj(Control c, Type T)
{
if (c.Parent.GetType() == T)
return c.Parent;
else
// keep looking
return GetParentObj(c.Parent, T);
}
Note above in my sample code, I commented out the use of above, but you can give this routine a try if you missing or guessing as to how many parents you have to bubble up to get the row is hit and miss for you.
As noted, you could also not use this .parent approach, and use the built in listview events, but, I often as noted, DUMP the huge mess of extra templates, and dump the HUGE complex event model of the lv, and just code my way right though this issue (to get that one all valuable row).
This is the only related post I've found so far on SO, not my exact issue, but close.
If I give focus to the first textbox on my page and then press TAB repeatedly until I reach the last control on my page the tab order works flawlessly, every control is given focus in the correct order.
BUT...
If I am actually filling out the form and I select a value in ANY dropdownlist, when I press TAB after selecting the value focus is given to the first textbox on my page rather than the next control after said dropdownlist.
I created a control that includes a label, a dropdownlist and an imagebutton because this list is used multiple times throughout my application. The control looks like this:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="OrganizationList.ascx.cs" Inherits="MyApp.Web.Controls.OrganizationList" %>
<%# Register src="UpdateProgress.ascx" tagname="UpdateProgress" tagprefix="uc1" %>
<%# Register src="Address.ascx" tagname="Address" tagprefix="uc2" %>
<asp:UpdatePanel ID="OrganizationUpdatePanel" runat="server" ChildrenAsTriggers="false" UpdateMode="Conditional">
<ContentTemplate>
<div style="margin: 0px;">
<div class="label"><asp:Label ID="Prompt" runat="server"></asp:Label>:</div>
<div class="field"><asp:DropDownList ID="Organizations" runat="server" Width="205" AutoPostBack="True" DataTextField="Name" DataValueField="Id"></asp:DropDownList> <asp:ImageButton id="AddOrganization" runat="server" ImageAlign="AbsMiddle" ToolTip="Add a new organization" onclick="AddOrganization_Click" /></div>
</div>
<asp:HiddenField ID="DummyButton" runat="server" />
<ajax:ModalPopupExtender ID="AddOrganizationModalPopupExtender" runat="server" BackgroundCssClass="modalBackground" CancelControlID="Cancel" DropShadow="true" PopupControlID="NewOrganization" RepositionMode="RepositionOnWindowResizeAndScroll" TargetControlID="DummyButton"></ajax:ModalPopupExtender>
<asp:Panel ID="NewOrganization" runat="server" CssClass="modalPopUp" Width="500">
<asp:Panel ID="OrganizationModalContent" runat="server" CssClass="modalContent" GroupingText="Add Organization" style="text-align: left; vertical-align: top;" Width="490">
<table border="0" cellpadding="2" cellspacing="0" width="100%">
<tr><td colspan="2"><asp:ValidationSummary ID="OrganizationValidationSummary" runat="server" CssClass="ValidationSummary" ValidationGroup="NewOrg" /></td></tr>
<tr><td class="label"><asp:RequiredFieldValidator ID="NameRequired" runat="server" ControlToValidate="Name" Display="Dynamic" ErrorMessage="Please provide the organization's name." SetFocusOnError="true" ValidationGroup="NewOrg">*</asp:RequiredFieldValidator>Organization Name:</td>
<td><asp:TextBox ID="Name" runat="server" MaxLength="100" Width="200"></asp:TextBox></td></tr>
<tr><td class="label"><asp:RequiredFieldValidator ID="PhoneRequired" runat="server" ControlToValidate="Phone" Display="Dynamic" ErrorMessage="Please provide the organization's phone number." SetFocusOnError="true" ValidationGroup="NewOrg">*</asp:RequiredFieldValidator>Phone:</td>
<td><ajax:MaskedEditExtender ID="PhoneMaskedEditExtender" runat="server" ClearTextOnInvalid="true" InputDirection="LeftToRight" Mask="(999)999-9999" MaskType="Number" MessageValidatorTip="true" PromptCharacter="_" TargetControlID="Phone"></ajax:MaskedEditExtender>
<asp:TextBox ID="Phone" runat="server" MaxLength="10" Width="200"></asp:TextBox></td></tr>
<tr><td class="label">Fax:<br />(optional)</td>
<td><ajax:MaskedEditExtender ID="FaxMaskedEditExtender" runat="server" ClearTextOnInvalid="true" InputDirection="LeftToRight" Mask="(999)999-9999" MaskType="Number" MessageValidatorTip="true" PromptCharacter="_" TargetControlID="Fax"></ajax:MaskedEditExtender>
<asp:TextBox ID="Fax" runat="server" MaxLength="10" Width="200"></asp:TextBox></td></tr>
<tr><td colspan="2"><uc2:Address ID="Address" runat="server" /></td></tr>
<tr><td colspan="2" style="text-align: right;"><asp:HiddenField ID="TypeOfList" runat="server" /><asp:Button ID="Submit" runat="server" CausesValidation="true" Text="Submit" onclick="Submit_Click" ValidationGroup="NewOrg" /><asp:Button ID="Cancel" runat="server" Text="Cancel" /></td></tr>
</table>
</asp:Panel>
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
<uc1:UpdateProgress ID="OrganizationUpdateProgress" runat="server" AssociatedUpdatePanelId="OrganizationUpdatePanel" />
Here's the TabIndex property on my control:
public short TabIndex
{
get { return Organizations.TabIndex; }
set { Organizations.TabIndex = value; AddOrganization.TabIndex = (short)(value + 1); }
}
Here's one of the references to the control on the parent page:
<uc3:OrganizationList ID="ReportedBy" runat="server" FieldLabel="Reported By" ListType="ReportedBy" TabIndex="105" />
Not sure what else you might want/need to see.
Browsers: IE8, IE9, IE10, FF 29.0.1
We've had some issues with IE versions above 9, so I added a HTTP Response Header to emulate IE9
My users claim that they primarily TAB to navigate in my app, so this is actually a higher priority bug than I expected. I don't know why the TabIndex is ignored once a value is selected.
Any ideas would be greatly appreciated.
UPDATE:
I do have 1 dropdownlist that is just an out-of-the-box dropdownlist, not a user control. That one works correctly. I can select a value in that list and then press TAB and the focus moves to the next control. So, can anyone help me figure out what is "wrong" with my user control that is causing this bug?
These two posts helped me figure out what I needed to do:
A postback via update panel resets TabIndex?
Accessing ToolKitScriptManager/ScriptManager from custom control
I added a SelectedIndexChanged event handler to my DropDownList in my control and set the focus back to the DropDownList. Here's the code:
protected void Organizations_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList ddl = (DropDownList)sender;
ScriptManager scriptMgr = ScriptManager.GetCurrent(Page);
scriptMgr.SetFocus(ddl);
}
Here's the control on the .aspx page:
<asp:DropDownList ID="Organizations" runat="server" Width="205" OnSelectedIndexChanged="Organizations_SelectedIndexChanged" AutoPostBack="True" DataTextField="Name" DataValueField="Id"></asp:DropDownList>
2014.05.22 UPDATE:
In my custom control I have an image button (+) named AddOrganization. In testing the fix yesterday we discovered that if a user clicked the '+' to add a new organization...after they saved the organization...the new value would get auto-selected (as expected) but the tabbing would get reset again. So I had to modify my button click event handler:
protected void Submit_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
// Do stuff...save organization
// update the list
BindOrganizationList();
// select the new value
Organizations.SelectedIndex = Organizations.Items.IndexOf(Organizations.Items.FindByText(OrganizationName.Text.ToUpper().Trim()));
// reset the form
ResetOrganizationForm();
OrganizationUpdatePanel.Update();
// set focus back to the dropdownlist
ScriptManager scriptMgr = ScriptManager.GetCurrent(Page);
scriptMgr.SetFocus(Organizations);
}
else
{
// Display friendly validation messages to user
}
}
I have the following DataList-
<td>
<asp:Label ID="Label3" runat="server" Text="Exclude"></asp:Label>
<asp:DataList runat="server" ID="excludeTextBox">
<ItemTemplate>
<br />
<asp:TextBox ID="myTextBox" runat="server" Text='<%# Container.DataItem.ToString() %>'></asp:TextBox>
</ItemTemplate>
</asp:DataList>
<td>
<asp:Label ID="Label4" runat="server" Text="Active"></asp:Label>
<asp:DataList runat="server" ID="activeCheck" >
<ItemTemplate>
<br />
<asp:CheckBox ID="CheckBox1" runat="server" Checked='<%# Container.DataItem.ToString().Equals("1") %>' OnCheckedChanged="CheckBox1_CheckedChanged" AutoPostBack="true" />
</ItemTemplate>
</asp:DataList>
</td>
So this is generating a textBox and checkBox for every row that exits in the database.
Currently there is no save action associated with the dataList, however if a user checks or unchecks one of the checkBoxes this calls a webservice and toggles the value in a data base table.
My question is, after a user has edited the text in some of the textBoxes and checked or unchecked some of the check boxes, how can I catch the changes in a save changes button. Because if the items have been dynamically created then technically they do not exist on the page. I want to do this without having a save option on ever row on the dataList.
protected void saveChanges_Click(object sender, EventArgs e)
{
// capture all edits and call update webservice method.
}
You'd essentially have to build the representation of the changes. Because there is no generic solution to this, I don't have any code, but here is the process:
As an item changes, you have to store what changed. You could store the indexes of the rows that actually changed in a hidden variable on the page, or store the changes in a JavaScript object. When save changes is clicked, on the client you then loop through these changes, and if they exist send them in bulk or one at a time.
If you had a hidden field with a value like 0,2,5 for each index that changed, you could easily find those items, grab the values from the form, and ship them off to the web service. Or, you could build a JavaScript object that has the changes like:
{ key: 2, checked: true, text:"My new text" }
Store these in an array, and push them up to the server through the web service.
<asp:GridView ID="GridView1" runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" OnClick="lclick">Buy</asp:LinkButton>
</ItemTemplate>
<edititemtemplate>
<asp:Textbox runat="server" id="txt"/>
<edititemtemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Is it possible to edit the GridView without the edititem Template?
I have done this before and I can only explain what I did
To make sure my edit controls is part of viewstate I added an empty form to the bottom of my page
<div id="myeditform">
<table>
<tr>
<td><asp:HiddenField runat="server" ID="myRowId" />
</td>
<td>... other controls </td>
<td>... Save button -- </td>
</tr>
<table>
</div>
The tr(s) has to match the number of columns in your grid or you can use colspan
Then
use jQuery or javascript to get the row (e.g. the nearest parent to the edit link clicked),
get the td(s) and pass the value of each td to the respective control in your edit form.
replace your <tr> with the content of the table in your edit form
when save is clicked, refresh the page to update gridview
I'm OK with C# and VB.nET
I have a DataList (Question) inside the ListView (Section). ListiView is to hold the sections. DataList holds the questions of a section. Let's say I have 3 sections, each section has 2 questions.
<asp:ListView ID="lvSection" runat="server">
<LayoutTemplate>
<div id="itemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<div>
<p><%#Eval("Section")%>
<asp:HiddenField ID="hfSectionId" runat="server" Value='<%#Eval("SectionId")%>' />
<hr />
</p>
</div>
<asp:DataList ID="dlQuestion" runat="server" >
<ItemTemplate>
<asp:Label ID="lblQuestion" runat="server" Text='<%# Eval("Question") %>'></asp:Label>
<asp:HiddenField ID="hfQuestionId" runat="server" Value='<%# Eval("QuestionId") %>' />
</ItemTemplate>
</asp:DataList>
</ItemTemplate>
</asp:ListView>
<br/>
<asp:Button runat="server" Text="Submit" onclick="Submit_Click" />
I'm trying to access the DataList dlQuestion when the button "Submit" is click like this:
Protected Sub Submit_Click(ByVal sender As Object, ByVal e As EventArgs)
'but I need to loop through all DataLists inside the ListView
'Maybe there are ways to get all the DataLists into a collection and then can loop through each one of them
'this will get only one DataList. Here's the pseudocode
Dim question As DataList = lvSection.FindControl("dlQuestion")
For Each item As DataListItem In quest.Items
Dim questionId As HiddenField = item.FindControl("hfQuestionId")
Next
End Sub
But it does not get anything back, question always gets nohting. I think it's because there are 3 DataList inside the ListView now, due to 3 sections, and it cannot find DataList dlQuestion anymore. How do I access these DataLists of ListView from the code behind? I need to loop through each control of the DataList .
Thank you.
You would need to do it as this:
for each item As ListViewDataItem in lvSection.Items
Dim list As DataList = item.FindControl("dlQuestion")
If (list IsNot Nothing) Then
For Each dlItem As DataListItem In quest.Items
Dim questionId As HiddenField = dlItem.FindControl("hfQuestionId")
Next
End If
Next
You have to access the data list in each item in the ListView first, not at the listview level.