Веб-элемент управления, встроенный в ASP.NET ListView, теряет состояние и передаваемые значения в обработчике события ItemDataBound при обратной передаче страницы - PullRequest
2 голосов
/ 24 июня 2011

Этот вопрос касается очень странного поведения веб-элементов управления. Подумайте о следующем, пожалуйста.

Сценарий

У меня есть веб-элемент управления, который используется для отображения некоторых данных. MyWebControl.ascx Файл имеет следующий вид:

<%@ Control ViewStateMode="Enabled" 
            Language="C#" 
            AutoEventWireup="true" 
            CodeFile="MyWebControl.ascx.cs" 
            Inherits="MyWebControl" %>

<div>
   <asp:Label ID="MyLabel1" runat="server"></asp:Label>
   <asp:Label ID="MyLabel2" runat="server"></asp:Label>
</div>

И код позади MyWebControl.ascx.cs:

public partial class MyWebControl : 
      System.Web.UI.UserControl {
   protected string str1;
   protected string str2;

   public string Str1 {
      get { return this.str1; }
      set { this.str1 = value; }
   }
   public string Str2 {
      get { return this.str2; }
      set { this.str2 = value; }
   }

   protected void Page_Load(object sender, EventArgs e) {
      this.MyLabel1.Text = this.str1;
      this.MyLabel2.Text = this.str2;
   }
}

Ну, я использую этот элемент управления внутри ListView в обычной веб-форме. Эта веб-форма называется Default.aspx, и после этого я показываю фрагмент кода, только тот, в котором находится ListView:

<%@ Page Title="My page" Language="C#" AutoEventWireup="true"
         CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register TagPrefix="my" TagName="MyWC" Src="~/MyWebControl.ascx" %>
...
...
<div>
   <asp:ListView ID="MyLV_ListView" runat="server" 
                 EnableViewState="true" 
                 OnPreRender="MyLV_ListView_PreRender" 
                 OnItemDataBound="MyLV_ListView_ItemDataBound">
      <ItemTemplate>
         <my:MyWC ID="WC_MyWC" runat="server" />
      </ItemTemplate>
   </asp:ListView>
</div>

Код этой веб-формы Default.aspx.cs:

public partial class _Default : System.Web.UI.Page {
   protected void Page_Load(object sender, EventArgs e) {
      if (!this.IsPostBack) {
         DAO d = new DAO(...); /* This object will contain some data */
                               /* Data are somehow paged, changing index, we have other data */
                               /* Please do not worry about paging, it works, problem is another */
         d.PageIndex = 0;
         this.ViewState["DAO"] = d; /* DAO is serializable */
         this.ViewState["PageIndex"] = 0;
         Data[] data = d.GetData(); /* Returns an array of objects representing my data */
                                    /* Custom way of storing data in objects */
                                    /* GetData use the PageIndex to get only a range of results */
         this.MyLV_ListView.DataSource = data;
         this.MyLV_ListView.DataBind();
      }
   }
   protected void MyLV_ListView_ItemDataBound(object sender, ListViewItemEventArgs e) {
      if (e.Item.ItemType == ListViewItemType.DataItem) {
         ((MyWebControl)e.Item.FindControl("WC_MyWC")).Str1 = 
            ((Data)e.Item.DataItem).DataField1;
         ((MyWebControl)e.Item.FindControl("WC_MyWC")).Str2 = 
            ((Data)e.Item.DataItem).DataField2;
      }
   }
   protected void MyLV_ListView_PreRender(object sender, EventArgs e) {
      if (this.IsPostBack)
         this.MyLV_ListView.DataBind();
   }
   // A TreeView will cause the page to reload, 
   // this is the handler called when a tree node is 
   // selected, the node is used to change the page 
   // for showing data in my listview.
   protected void MyLVPaging_TreeView_SelectedNodeChanged(object sender, EventArgs e) {
      this.ViewState["PageIndex"] = int.Parse(this.MyLVPaging_TreeView.SelectedNode.Value);
      ((DAO)this.ViewState["DAO"]).PageIndex = (int)this.ViewState["PageIndex"];
      // After setting the new index, GetData will return another set of results
      this.MyLV_ListView.DataSource = ((DAO)this.ViewState["DAO"]).GetData();
   }
}

проблема

В чем моя проблема ???

Хорошо, когда страница загружается в первый раз, я вижу некоторые данные в виде списка, так что все работает.

Когда я выбираю другую страницу, я заставляю страницу перезагружаться и отправлять обратно ... my ListView покажет правильное количество записей, но без данных на нем. Веб-элемент управления размещается столько раз, сколько количество извлеченных записей данных, но в веб-элементе управления нет данных.

Результаты отладки

Я провел несколько сеансов отладки, и вот что происходит, пожалуйста, следуйте за мной, это ОЧЕНЬ ВАЖНО понять, я думаю.

Страница загружается впервые

1) Load выполняется метод страницы.

2) ListView s ItemDataBound обработчик событий выполняется. Ну, учитывая, что у меня есть три элемента в массиве Data[] в DataSource, обработчик вызывается три раза. Проверяя значения при отладке метода, я вижу, что ((Data)e.Item.DataItem).DataField1 и ((Data)e.Item.DataItem).DataField2 - это правильные значения, полученные из связанного источника данных в ListView. После выполнения метода курсор отладчика перемещается в MyWebControl.ascx.cs к методу Page_Load элемента управления. Я вижу, проверяя переменные, что это происходит:

protected void Page_Load(object sender, EventArgs e) {
   this.MyLabel1.Text = this.str1; // <-- this.str1 has the correct value loaded in the list view itemdatabound event handler.
   this.MyLabel2.Text = this.str2; // <-- this.str2 has the correct value loaded in the list view itemdatabound event handler.
}

3) Вызывается метод ListView PreRender.

4) Страница отображается и все в порядке!

Страница отправляет обратно после выбора узла в TreeView

1) Load метод страницы выполняется, но он ничего не делает.

2) MyLVPaging_TreeView_SelectedNodeChanged выполнено. Здесь я вижу, что все имеет правильное значение:

protected void MyLVPaging_TreeView_SelectedNodeChanged (отправитель объекта, EventArgs e) { this.ViewState ["PageIndex"] = int.Parse (this.MyLVPaging_TreeView.SelectedNode.Value); // <- новая страница ((DAO) this.ViewState ["DAO"]). PageIndex = (INT) this.ViewState [ "PageIndex"]; // <- индекс сохранен // После установки нового индекса, GetData вернет другой набор результатов this.MyLV_ListView.DataSource = . ((ДАО) this.ViewState [ "ДАО"]) GetData (); // <- источник данных изменен } </p>

3) ListView s ItemDataBound обработчик событий выполняется. Что ж, здесь при отладке я вижу то же, что и раньше, я имею в виду, что я вижу, что новые значения, новые Data были связаны с ListView, фактически я могу проверять новые значения, которые назначены элементу шаблона , Очевидно, что, как и прежде, после выполнения метода курсор отладчика перемещается в MyWebControl.ascx.cs к методу Page_Load элемента управления. ЗДЕСЬ ЗЛО: Я вижу следующее:

protected void Page_Load(object sender, EventArgs e) {
   // Note: inspecting this.IsPostBack I get true!
   this.MyLabel1.Text = this.str1; // <-- this.str1 is null and also this.MyLabel1.Text is null.
   this.MyLabel2.Text = this.str2; // <-- this.str2 is null and also this.MyLabel1.Text is null. view itemdatabound event handler.
}

Хорошо !!! ЭТО ПОТЕРЯЕТ ГОСУДАРСТВО И ТАКЖЕ ЦЕННОСТИ ПРОЙДЕН ИЗ ВИДА СПИСКА HANDLER !!!!!!!!!

Мой вопрос

Что, черт возьми, происходит ???

Примечания

1) Пожалуйста, не зацикливайтесь на том, как мне удалось связать данные для событий Load и PreRender ... У меня такое ощущение, что это не ошибка! Я думаю, это связано с жизненным циклом страницы. Однако, если вы считаете, что это важная деталь, дайте мне знать.

2) DAO и его функция GetData() - это способ дать вам как можно быстрее понять сценарий, который немного более четко сформулирован, но с той же точной структурой, которую я показал здесь.

Спасибо за вашу помощь.

1 Ответ

2 голосов
/ 24 июня 2011

Из моих комментариев:

Почему бы вам не переместить this.MyLV_ListView.DataBind () из PreRender в SelectedNodeChanged?И зачем вам поля str1 и str2 в вашем UserControl?Свойство Str1 должно получить / установить this.MyLabel1.Text, а свойство Str2 должно получить / установить this.MyLabel2.Text, поскольку текст меток уже хранится в ViewState.

Andry:

... так что такие настройки не должны быть сделаны в Load?

Me:

вам не нужно ждатьдля любого события, чтобы произойти, чтобы установить текст метки.Свойства должны напрямую указывать на соответствующие метки, поэтому ваши поля str1 и str2 являются избыточными.Они будут расположены в конце жизненного цикла страницы, в отличие от свойства Text меток, которые автоматически сохраняются в ViewState через обратные передачи.

...