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

时间:2015-06-11 15:08:03

标签: 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>

然后是背后的代码:

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,
}

2 个答案:

答案 0 :(得分:6)

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

例如,您可以在Page_Load事件处理程序中执行此操作。

您可以存储在ViewState中重新创建控件树所需的任何数据,以便在PostBack上可用。

我不确定你的意思:

  

...当回发发生时,行返回到ItemTemplate

我希望EditIndex在回发后保持不变,除非你明确地修改它,因此编辑行不变,并且要使用EditItemTemplate。

如果您可以发布说明问题的简化样本,我(或其他人)可能会提供进一步的帮助。

<强>更新

根据发布的代码,您需要在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元素的逻辑。

答案 1 :(得分:1)

您应该使用内部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;
}