How do you get values ​​from dynamic controls in a ListView when posting back?

I recently had to go back to working with web form code and there was a problem trying to update an existing page that shows a list of survey responses.

I have a ListView showing the details of the people who responded to the survey. When you click on the icon in the line, the line of which enters the editing mode and displays your information (name, email address, etc.) as input fields.

So far so good, now I need to add questions and answers for this survey and this person. Writing them is easy, but when the postback occurs, the string returns to the ItemTemplate and the controls go away.

I know with the help of dynamic controls that you must create in Page_Init, so web forms can reassemble them, but here the controls are not created until the ListEtem ItemEditing event and the data stored in the ItemUpdating event.

I focused on how I can make controls in the right place with the right data at the right point in the life cycle. Right now I'm trying to pull the values ​​from Request.Form, but with CheckboxList it's hard to understand what was selected.

Edit: A new example that should actually run

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="QuestionExample.aspx.cs" Inherits="QuestionExample" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <table> <asp:ListView runat="server" ID="LV" OnItemEditing="LV_OnItemEditing" OnItemUpdating="LV_OnItemUpdating"> <LayoutTemplate> <tr> <td><asp:PlaceHolder runat="server" ID="itemPlaceHolder"></asp:PlaceHolder></td> </tr> </LayoutTemplate> <ItemTemplate> <asp:LinkButton ID="EditButton" runat="server" CommandName="Edit" Text="Edit"/> ID: <asp:Label runat="server" ID="lblLeadID" Text="<%# ((Lead)Container.DataItem).LeadID %>" /> Name: <%# ((Lead)Container.DataItem).Name %><br/> </ItemTemplate> <EditItemTemplate> <asp:LinkButton ID="UpdateButton" runat="server" CommandName="Update" Text="Update" /> ID: <asp:Label runat="server" ID="lblLeadID" Text="<%# ((Lead)Container.DataItem).LeadID %>" /> <asp:Label runat="server">Name</asp:Label> <asp:TextBox runat="server" id="tbName" Text="<%# ((Lead)Container.DataItem).Name %>"></asp:TextBox> <asp:Panel runat="server" id="pnlQuestions"></asp:Panel> </EditItemTemplate> </asp:ListView> </table> </form> </body> </html> 

Then the code is behind:

 using System; using System.Collections.Generic; using System.Linq; using System.Web.UI; using System.Web.UI.WebControls; public partial class QuestionExample : Page { #region fake data protected List<Question> Questions = new List<Question>() { new Question { QuestionID = 0, QuestionType = QuestionType.Textbox, QuestionText = "TextBox", Options = null }, new Question { QuestionID = 1, QuestionType = QuestionType.DropDownList, QuestionText = "DDL", Options = new List<string> { "A", "B", "C"} }, }; protected List<Lead> Leads = new List<Lead> { new Lead { LeadID = 0, Name = "Bob", Answers = new Dictionary<string, string> { { "TextBox", "Hi" }, { "DDL", "B" } } }, new Lead { LeadID = 1, Name = "Fred", Answers = new Dictionary<string, string> { { "TextBox", "Stuff" }, { "DDL", "C" } } }, }; #endregion protected void Page_Load(object sender, EventArgs e) { LV.DataSource = Leads; LV.DataBind(); } protected void LV_OnItemEditing(object sender, ListViewEditEventArgs e) { LV.EditIndex = e.NewEditIndex; LV.DataBind(); var leadLabel = (Label)LV.Items[e.NewEditIndex].FindControl("lblLeadID"); var leadID = int.Parse(leadLabel.Text); var panel = (Panel)LV.Items[e.NewEditIndex].FindControl("pnlQuestions"); var lead = Leads.First(l => l.LeadID == leadID); foreach (var answer in lead.Answers) { var question = Questions.First(q => q.QuestionText == answer.Key); panel.Controls.Add(CreatQuestionControl(question, lead, true)); } } protected Control CreatQuestionControl(Question question, Lead lead, bool setAnswers) { Control result = null; switch (question.QuestionType) { case QuestionType.Textbox: var tb = new TextBox(); if (setAnswers) { var answer = lead.Answers[question.QuestionText]; tb.Text = answer; } result = tb; break; case QuestionType.DropDownList: var ddl = new DropDownList { DataSource = question.Options }; ddl.DataBind(); if (setAnswers) { var answer = lead.Answers[question.QuestionText]; ddl.SelectedValue = answer; } result = ddl; break; } return result; } protected void LV_OnItemUpdating(object sender, ListViewUpdateEventArgs e) { // Get input data here somehow LV.EditIndex = -1; } } public class Lead { public int LeadID { get; set; } public string Name { get; set; } public Dictionary<string, string> Answers { get; set; } } public class Question { public int QuestionID { get; set; } public string QuestionText { get; set; } public QuestionType QuestionType { get; set; } public List<string> Options { get; set; } } public enum QuestionType { Textbox = 0, DropDownList = 1, } 
+6
source share
2 answers

Without seeing the code, it is difficult to give a complete answer, but basically you need to recreate the control tree, including dynamically created controls, in the postback.

You can do this, for example, in the Page_Load event handler.

You can save all the data needed to recreate the control tree in ViewState so that it is available in PostBack.

I'm not sure what you mean:

... when postback returns, the string returns to ItemTemplate

I would expect EditIndex to remain unchanged after the postback, unless you explicitly change it, and so the edit line does not change and EditItemTemplate will be used.

If you can post a simplified sample that illustrates your problem, I (or someone else) can help in the future.

UPDATE

Based on the published code, you need to recreate the dynamic controls for the ItemUpdating event handler and add them to the control tree in the same order as they were created in the ItemEditing event handler. That is, you probably need to repeat the code:

 var questionPannel = lL.Items[e.ItemIndex].FindControl("lead_table") as HtmlTable; var campaignQuestionsTableRow = questionPannel.FindControl("campaign_questions") as HtmlTableRow; var questions = GetQuestions(); foreach(var question in questions) { // This builds a WebControl for the dynamic question var control = CreateQuestionControl(question); campaignQuestionsTableRow.AddControl(control); } 

UPDATE 2

When upgrading, you need to recreate the dynamic controls on PostBack. Change the Page_Load event handler to something like:

 protected void Page_Load(object sender, EventArgs e) { LV.DataSource = Leads; LV.DataBind(); if (IsPostBack) { if (LV.EditIndex >= 0) { var leadLabel = (Label)LV.Items[LV.EditIndex].FindControl("lblLeadID"); var leadID = int.Parse(leadLabel.Text); var panel = (Panel)LV.Items[LV.EditIndex].FindControl("pnlQuestions"); var lead = Leads.First(l => l.LeadID == leadID); foreach (var answer in lead.Answers) { var question = Questions.First(q => q.QuestionText == answer.Key); panel.Controls.Add(CreatQuestionControl(question, lead, true)); } } } } 

In the first Postback (click Edit), EditIndex will be -1, and dynamic controls will be created in the OnItemEditing handler.

In the second Postback (click Refresh), EditIndex will be the index of the line you are editing, and you need to recreate the dynamic controls in Page_Load. If you do, you will find your published values ​​when you go to the OnItemUpdating event handler.

Although I think it’s worth understanding how to use dynamic controls in this way, in a production application I would probably use a repeater inside EditItemTemplate, as suggested by Elisheva Wasserman. The repeater may contain a UserControl that encapsulates the logic to hide / show user interface elements based on the type of question.

+6
source

You should use an internal relay or listView, which will be attached to the item_edit event, instead of re-creating the dynamics, it controls each entry. in the following way:

 <asp:ListView ID="lL" runat="server" OnItemEditing="lL_ItemEditing" OnItemUpdating="lL_ItemUpdating"> <itemtemplate> Read only view here... </itemtemplate> <edititemtemplate> <asp:LinkButton D="UpdateButton" runat="server" CommandName="Update" /> <table runat="server" ID="lead_table" class="lead_table"> <tr id="Tr1" style="background-color:#EEEEEE;" runat="server" > <td>Name: <asp:TextBox ID="Name" /></td> Other Fixed controls... </tr> <tr runat="server" ID ="campaign_questions"> <asp:Repeater id='repetur1' runat="server"> <ItemTemplate> <asp:Label id='lblCaption' runat='server" Text='<%Eval("QuestionTitle")%>' /> <asp:TextBox id='name' runat="server" /> </ItemTemplate> </asp:Repeater> </tr> </table> 

by code

 protected void lL_ItemEditing(object sender, ListViewEditEventArgs e) { var questionPannel = lL.Items[e.ItemIndex].FindControl("lead_table") as HtmlTable; var campaignQuestionsTableRow = questionPannel.FindControl("campaign_questions") as HtmlTableRow; // bind fixed controls var lead = GetLead(); var nameControl = ((TextBox)lL.Items[e.ItemIndex].FindControl("Name")) nameControl.Text = lead.Name; // bind dynamic controls var questions = GetQuestions(); var rep= (Repeater)lL.Items[e.ItemIndex].FindControl("repeatur1"); rep.DataSource=questions; rep.DataBind(); } protected void lL_ItemUpdating(object sender, ListViewUpdateEventArgs e) { // Get fixed fields // var lead = GetLead(); lead.Name = ((TextBox)lL.Items[e.ItemIndex].FindControl("Name")).Text.Trim(); Repeater rep=(Repeater)e.FindControl("repeatur1")); foreach (RepeaterItem item in rep.Items) { TextBox t=item.FindControl("txtName") as TextBox; //do your work here } // Switch out of edit mode lL.EditIndex = -1; } 
+1
source

Source: https://habr.com/ru/post/988935/


All Articles