如何在回发时从列表视图中的动态控件中获取值

本文关键字:视图 动态控件 获取 列表 | 更新日期: 2023-09-27 17:55:33

我最近不得不回到处理网络表单代码,并且在尝试更新显示调查响应列表的现有页面时遇到了问题。

我有一个列表视图,显示已回答调查的人员的详细信息。当您单击该行中的图标时,该行进入编辑模式并将其信息(姓名、电子邮件等)显示为输入框。

到目前为止一切顺利,现在我需要为该调查和那个人添加问题和答案。写出它们很容易,但是当回发发生时,行将返回到 ItemTemplate,控件将消失。

我知道使用动态控件,您应该创建 in Page_Init以便 Web 表单可以重新绑定它们,但在这里,直到 ListItem ItemEditing 事件和数据保存在 ItemUpdate 事件中,才会创建控件。

我纠结于如何在生命周期的正确点使用正确的数据在正确的位置制作控件。现在我正在尝试从 Request.Form 中提取值,但使用复选框列表,很难弄清楚选择了什么。

编辑:应该实际运行的新示例

<%@ 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 中存储重新创建控件树所需的任何数据,以便它在回发上可用。

我不确定你的意思是:

。发生回发时,该行将返回到项模板

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

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

更新

根据发布的代码,您需要在 ItemUpdate 事件处理程序中重新创建动态控件,并按照在 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

更新时,需要在回发上重新创建动态控件。 将 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));
            }
        }
    }
}

在第一次回发(单击"编辑")时,EditIndex 将为 -1,动态控件在 OnItemEditing 处理程序中创建。

在第二次回发(单击"更新")时,EditIndex 将成为您正在编辑的行的索引,您需要在 Page_Load 中重新创建动态控件。 如果执行此操作,则会在到达 OnItemUpdate 事件处理程序时找到已发布的值。

虽然我认为了解如何以这种方式使用动态控件是值得的,但在生产应用程序中,我可能会按照Elisheva Wasserman的建议在EditItemTemplate中使用中继器。 转发器可能包含一个用户控件,该控件封装了基于问题类型隐藏/显示 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;
}