如何在回发时从ListView中的动态控件获取值?

Man*_*101 6 c# asp.net webforms

我最近不得不回到处理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>
Run Code Online (Sandbox Code Playgroud)

然后代码背后:

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,
}
Run Code Online (Sandbox Code Playgroud)

Joe*_*Joe 6

在没有看到代码的情况下,很难给出完整的答案,但基本上你需要在回发时重新创建控制树,包括动态创建的控件.

例如,您可以在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); 
}
Run Code Online (Sandbox Code Playgroud)

更新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));
            }

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在第一个Postback(单击Edit)上,EditIndex将为-1,并且在OnItemEditing处理程序中创建动态控件.

在第二个Postback(单击Update)上,EditIndex将是您正在编辑的行的索引,您需要在Page_Load中重新创建动态控件.如果这样做,当您到达OnItemUpdating事件处理程序时,您将找到您发布的值.

虽然我认为理解如何以这种方式使用动态控件是值得的,但在生产应用程序中,我可能会按照Elisheva Wasserman的建议在EditItemTemplate中使用Repeater.Repeater可以包含一个UserControl,它封装了基于问题类型隐藏/显示UI元素的逻辑.