2009-03-13 3 views
2

У ViewState страницы ASP.NET, похоже, есть проблемы с динамически удаленными элементами управления и значениями в них.Элементы управления ViewState и динамически удалены

Давайте возьмем следующий код в качестве примера:

ASPX:

<form id="form1" runat="server"> 
<div> 
    <asp:Panel runat="server" ID="controls" /> 
</div> 
</form> 

CS:

protected void Page_Init(object sender, EventArgs e) { 
    Button b = new Button(); 
    b.Text = "Add"; 
    b.Click +=new EventHandler(buttonOnClick); 
    form1.Controls.Add(b); 
    Button postback = new Button(); 
    postback.Text = "Postback"; 
    form1.Controls.Add(postback); 
} 

protected void Page_Load(object sender, EventArgs e) { 
    if (ViewState["controls"] != null) { 
     for (int i = 0; i < int.Parse(ViewState["controls"].ToString()); i++) { 
      controls.Controls.Add(new TextBox()); 
      Button remove = new Button(); 
      remove.Text = "Remove"; 
      remove.Click +=new EventHandler(removeOnClick); 
      controls.Controls.Add(remove); 
      controls.Controls.Add(new LiteralControl("<br />")); 
     } 
    } 
} 

protected void removeOnClick(object sender, EventArgs e) { 
    Control s = sender as Control; 
    //A hacky way to remove the components around the button and the button itself 
    s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s) + 1]); 
    s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s) - 1]); 
    s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s)]); 
    ViewState["controls"] = (int.Parse(ViewState["controls"].ToString()) - 1).ToString(); 
} 

protected void buttonOnClick(object sender, EventArgs e) { 
    if (ViewState["controls"] == null) 
     ViewState["controls"] = "1"; 
    else 
     ViewState["controls"] = (int.Parse(ViewState["controls"].ToString()) + 1).ToString(); 
    controls.Controls.Add(new TextBox()); 
} 

Тогда, скажем создать 4 управления и вставьте следующие значения:

[ 1 ] [ 2 ] [ 3 ] [ 4 ] 

Мы хотим удалить второй элемент управления; после удаления второго управления выхода:

[ 1 ] [ 3 ] [ 4 ] 

который, что мы хотим. К сожалению, в последующем PostBack, список становится:

[ 1 ] [ ] [ 3 ] 

Итак, мой вопрос, почему это происходит? Насколько я читал, ViewState должен сохранять свойства элементов управления в зависимости от их индексов, а не от фактических элементов управления.

ответ

6

Пара вещей. Будут ли элементы управления загружены их идентификатором или индексом, зависит от атрибута ViewStateModeById. По умолчанию это значение false (значение load by index).

Однако текстовые поля обрабатываются по-разному. Их состояние представления не содержит входных значений, если они не отключены или невидимы. Свойство text переписывается опубликованными значениями, используя их идентификаторы. Поскольку вы не управляете идентификаторами текстовых полей, это то, что происходит.

После того, как вы добавили четыре элемента управления, у вас есть четыре текстовых поля: ctrl0, ctrl1, ctrl2 и ctrl3 со значениями 1, 2, 3 и 4 соответственно.

Затем вы удаляете поле ctrl1, и клиент получает три поля: ctrl0, ctrl2 и ctrl3 с соответствующими значениями. Теперь, когда вы выполняете обратную передачу, эти три значения передаются в форме ctrl0 = 1 & ctrl2 = 3 & ctrl3 = 4.

Затем на странице Page_Load вы создаете три элемента управления, на этот раз: ctrl0, ctrl1, ctrl2 без значений.

Framework вызывает LoadRecursive для загрузки состояний просмотра, а затем ProcessPostData для назначения входных значений. Он видит представленные ctrl0 и ctrl2, находит элементы управления с одним и тем же идентификатором и присваивает им значения 1 и 3. Он не находит ctrl3, поэтому он пропускает его.Остальные ctrl1 просто переносятся без какого-либо значения.

В качестве примера рассмотрим это решение (не самое лучшее):

protected void Page_Init(object sender, EventArgs e) 
{ 
    Button b = new Button(); 
    b.Text = "Add"; 
    b.Click += new EventHandler(buttonOnClick); 
    form1.Controls.Add(b); 

    Button postback = new Button(); 
    postback.Text = "Postback"; 
    form1.Controls.Add(postback); 
} 

protected void Page_Load(object sender, EventArgs e) 
{ 
    if (ViewState["controls"] != null) 
    { 
     List<string> ids = (List<string>)ViewState["controls"]; 

     for (int i = 0; i < ids.Count; i++) 
     { 
      TextBox textbox = new TextBox(); 
      textbox.ID = string.Format("txt_{0}", ids[i]); 
      textbox.Text = textbox.ID; 
      controls.Controls.Add(textbox); 

      Button remove = new Button(); 
      remove.Text = "Remove"; 
      remove.Click += new EventHandler(removeOnClick); 
      remove.ID = ids[i]; 
      controls.Controls.Add(remove); 

      controls.Controls.Add(new LiteralControl("<br />")); 
     } 
    } 
} 

protected void removeOnClick(object sender, EventArgs e) 
{ 
    Control btn = sender as Control; 

    List<string> ids = (List<string>)ViewState["controls"]; 
    ids.Remove(btn.ID); 

    //A hacky way to remove the components around the button and the button itself 
    btn.Parent.Controls.Remove(btn.Parent.Controls[btn.Parent.Controls.IndexOf(btn) + 1]); 
    btn.Parent.Controls.Remove(btn.Parent.Controls[btn.Parent.Controls.IndexOf(btn) - 1]); 
    btn.Parent.Controls.Remove(btn); 

    ViewState["controls"] = ids; 
} 

protected void buttonOnClick(object sender, EventArgs e) 
{ 
    List<string> ids; 

    if (ViewState["controls"] == null) 
     ids = new List<string>(); 
    else 
     ids = (List<string>)ViewState["controls"]; 

    string id = Guid.NewGuid().ToString(); 
    TextBox textbox = new TextBox(); 
    textbox.ID = string.Format("txt_{0}", id); 
    textbox.Text = textbox.ID; 
    controls.Controls.Add(textbox); 

    Button remove = new Button(); 
    remove.Text = "Remove"; 
    remove.Click += new EventHandler(removeOnClick); 
    remove.ID = id; 
    controls.Controls.Add(remove); 

    controls.Controls.Add(new LiteralControl("<br />")); 

    ids.Add(id); 

    ViewState["controls"] = ids; 
} 
+0

у меня есть связанный проблема - у меня есть GridView зависит от комбо постбэка. Когда вы меняете комбо, сетка обновляется с данными для нового значения комбо. Но если вы нажмете обновление, сетка вернется к данным по умолчанию для первого элемента в комбо, но комбо сбрасывается на элемент, который был выбран при обновлении, поэтому сетка и комбо не синхронизированы. Я попытался привязать и установить комбо для первого элемента в 'Page_Load if! Postback', а html показывает, что верхний элемент украшен' selected = "selected" ', но некоторые javascript затем меняют его на нестандартный пункт! Связано ли это? – Chris

0

Вы делаете половину своего кода в Page_Load и половину кода в своих событиях обратной передачи. Где-то в этом беспорядке вы столкнулись с конфликтами. Тот факт, что они не отображаются до тех пор, пока вторая обратная передача не сообщит мне, что в вашей логике есть некоторый пробег.

Я не уверен, где именно происходит проблема, но обработка ViewState не самая забавная вещь в мире, когда вы делаете пользовательскую ViewState crud. Мне пришлось бы перестроить приложение и установить условия просмотра, чтобы увидеть, что происходит, но я уверен, что это несоответствие импеданса между вашим кодом Page_Load и обработчиками событий.

Смежные вопросы