2015-06-11 21 views
6

Gần đây tôi đã phải quay lại để xử lý mã webform và đã gặp sự cố khi cố cập nhật trang hiện tại hiển thị danh sách các câu trả lời khảo sát.Làm thế nào để bạn nhận được các giá trị từ các điều khiển động trong một ListView trên postback?

Tôi có một ListView hiển thị chi tiết về những người đã trả lời bản khảo sát. Khi bạn nhấp vào biểu tượng trong hàng có hàng nhập chế độ chỉnh sửa và hiển thị thông tin của họ (tên, email, v.v ...) làm hộp nhập.

Cho đến giờ rất tốt, bây giờ tôi cần thêm câu hỏi và câu trả lời cho cuộc khảo sát đó và người đó. Viết chúng ra dễ dàng, nhưng khi postback xảy ra hàng sẽ trở lại ItemTemplate và các điều khiển đã biến mất.

Tôi biết với các điều khiển động mà bạn phải tạo trong Page_Init để các biểu mẫu web có thể rebind chúng, nhưng ở đây các điều khiển không được tạo cho đến khi sự kiện ItemItem ItemEditing và dữ liệu được lưu trong sự kiện ItemUpdating.

Tôi bị mắc kẹt về cách tôi có thể thực hiện các điều khiển ở đúng nơi với đúng dữ liệu vào đúng điểm trong vòng đời. Ngay bây giờ tôi đang cố gắng để kéo các giá trị từ Request.Form nhưng với một CheckboxList thật khó để tìm ra những gì đã được chọn.

Edit: ví dụ mới mà thực sự nên chạy

<%@ 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> 

Sau đó, các mã sau:

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

Trả lời

6

Nếu không nhìn thấy mã của bạn thật khó để đưa ra một câu trả lời đầy đủ, nhưng về cơ bản bạn cần phải tạo lại cây điều khiển, bao gồm các điều khiển được tạo động, trên postback.

Bạn có thể thực hiện việc này, ví dụ, trong trình xử lý sự kiện Page_Load.

Bạn có thể lưu trữ bất kỳ dữ liệu nào bạn cần để tạo lại cây điều khiển trong ViewState để có sẵn trên PostBack.

Tôi không chắc chắn những gì bạn có nghĩa là bởi:

... khi postback xảy ra hàng đi trở lại ItemTemplate

Tôi mong chờ EditIndex ở lại không thay đổi sau khi một postback , trừ khi bạn sửa đổi nó một cách rõ ràng và do đó hàng chỉnh sửa sẽ không thay đổi, và EditItemTemplate sẽ được sử dụng.

Nếu bạn có thể đăng mẫu đơn giản minh họa sự cố của bạn, tôi (hoặc người khác) có thể giúp đỡ thêm.

CẬP NHẬT

Dựa trên mã được đăng, bạn cần phải tạo các điều khiển năng động trong xử lý sự kiện ItemUpdating và thêm chúng vào cây điều khiển theo thứ tự giống như bạn tạo ra chúng trong xử lý sự kiện ItemEditing. I E. bạn có thể cần phải lặp lại các mã:

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

Khi cập nhật, bạn cần phải tạo các điều khiển động trên PostBack.Thay đổi xử lý sự kiện Page_Load của bạn để một cái gì đó như:

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

     } 
    } 
} 

Trên Postback đầu tiên (click vào Edit), EditIndex sẽ là -1, và các điều khiển động được tạo ra trong xử lý OnItemEditing.

Trên Postback thứ hai (bấm vào Cập nhật), EditIndex sẽ là chỉ mục của dòng bạn đang chỉnh sửa và bạn cần tạo lại các điều khiển động trong Page_Load. Nếu bạn làm điều này, bạn sẽ tìm thấy các giá trị đã đăng của mình khi bạn truy cập trình xử lý sự kiện OnItemUpdating.

Mặc dù tôi nghĩ rằng nó đáng giá để hiểu cách sử dụng điều khiển động theo cách này, trong ứng dụng sản xuất, tôi có thể sử dụng Bộ lặp trong EditItemTemplate theo đề xuất của Elisheva Wasserman. Các Repeater có thể chứa một UserControl mà đóng gói logic để ẩn/hiển thị các yếu tố giao diện người dùng dựa trên loại câu hỏi.

+0

OK, tôi đã thêm một ví dụ. những gì tôi có nghĩa là bằng cách trở lại ItemTemplate nên hy vọng là một chút rõ ràng hơn, câu hỏi chỉ được hiển thị trong EditItemTemplate bạn điền chúng vào, lưu của mình và sau đó trang hiển thị ItemTemplate mà không có những điều khiển. – Mant101

+0

Tôi đã thử điều này, nhưng họ không có các giá trị được đăng lại trong đó. Từ những gì tôi đã có thể tìm thấy tôi cần phải tạo chúng trong Page_Load cho dữ liệu để rebind, và làm cho chúng trong iL_ItemUpdating là quá muộn trong vòng đời. – Mant101

+0

@ Mant101 - Page_Load sẽ là một nơi tốt hơn để tạo chúng. Có thể bạn có thể kiểm tra 'IsPostBack && (EditIndex> = 0)' trong Page_Load và tạo lại các điều khiển ở đó. Nếu bạn có thể cung cấp một mẫu hoàn chỉnh tối thiểu để chứng minh sự cố (http://stackoverflow.com/help/mcve) Tôi hoặc người khác sẽ có thể trợ giúp. – Joe

1

Bạn nên sử dụng một bên trong Repeater hoặc listView, sẽ được liên kết trên sự kiện item_edit, thay vì tạo lại các điều khiển động mỗi bài đăng. theo cách này:.

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

trên mã đằng sau

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; 
} 
+0

Các câu hỏi có thể là các điều khiển hoàn toàn khác nhau, một khảo sát có thể có danh sách nút radio, một hộp văn bản và vân vân. Vì vậy, tôi đoán lặp lại sẽ cần tất cả các loại câu hỏi trong đó, và sau đó ẩn những người không được sử dụng cho câu hỏi đó? – Mant101

+0

Có, mặc dù nó có vẻ phức tạp, nó là một lựa chọn tốt hơn, các điều khiển được tạo động không được lưu trữ trong các lỗi của Viewstate và couse lot. bạn có thể sử dụng đánh dấu trong đánh dấu để thay đổi visibilty của điều khiển không cần thiết. Sử dụng thuộc tính máy chủ Có thể nhìn thấy và không thích hợp với khách hàng, vì vậy asp.net sẽ không tạo ra các điều khiển hiddne chút nào. –

Các vấn đề liên quan