[英]Listview with Dynamic Controls - Dynamic controls disappear after postback
[英]How do you get values from dynamic controls in a ListView on postback?
我最近不得不回到處理webforms代碼並且遇到了一個問題,試圖更新顯示調查回復列表的現有頁面。
我有一個ListView顯示已經回答調查的人的詳細信息。 當您單擊該行進入編輯模式的行中的圖標並將其信息(名稱,電子郵件等)顯示為輸入框時。
到目前為止一切順利,現在我需要為該調查和那個人添加問題和答案。 寫出來很容易,但是當回發發生時,行返回到ItemTemplate並且控件消失了。
我知道使用動態控件你應該在Page_Init中創建,所以webforms可以重新綁定它們,但是在這里直到ListItem ItemEditing事件才創建控件,並且在ItemUpdating事件中保存數據。
我堅持認為如何在生命周期的正確位置使用正確的數據將控件放在正確的位置。 現在我正試圖從Request.Form中提取值,但是使用CheckboxList,很難弄清楚已經選擇了什么。
編輯:應該實際運行的新示例
<%@ 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>
然后代碼背后:
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,
}
在沒有看到代碼的情況下,很難給出完整的答案,但基本上你需要在回發時重新創建控制樹,包括動態創建的控件。
例如,您可以在Page_Load事件處理程序中執行此操作。
您可以存儲在ViewState中重新創建控件樹所需的任何數據,以便在PostBack上可用。
我不確定你的意思是:
...當回發發生時,行返回到ItemTemplate
我希望EditIndex在回發后保持不變,除非你明確地修改它,因此編輯行保持不變,並且要使用EditItemTemplate。
如果您可以發布一個簡化的示例來說明您的問題,我(或其他人)可能會進一步提供幫助。
UPDATE
根據發布的代碼,您需要在ItemUpdating事件處理程序中重新創建動態控件,並按照在ItemEditing事件處理程序中創建它們的順序將它們添加到控件樹中。 即你可能需要重復代碼:
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);
}
更新2
更新時,需要在PostBack上重新創建動態控件。 將Page_Load事件處理程序更改為:
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));
}
}
}
}
在第一個Postback(單擊Edit)上,EditIndex將為-1,並且在OnItemEditing處理程序中創建動態控件。
在第二個Postback(單擊Update)上,EditIndex將是您正在編輯的行的索引,您需要在Page_Load中重新創建動態控件。 如果這樣做,當您到達OnItemUpdating事件處理程序時,您將找到您發布的值。
雖然我認為理解如何以這種方式使用動態控件是值得的,但在生產應用程序中,我可能會按照Elisheva Wasserman的建議在EditItemTemplate中使用Repeater。 Repeater可以包含一個UserControl,它封裝了基於問題類型隱藏/顯示UI元素的邏輯。
您應該使用內部Repeater或listView,它將綁定在item_edit事件上,而不是每個帖子重新創建動態控件。 這條路:
<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>
代碼背后
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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.