Почему события динамически добавленного пользовательского элемента управления не запускаются при обратной передаче? - PullRequest
2 голосов
/ 05 августа 2011

Существует множество похожих вопросов по SO ( 1 , 2 , 3 , 4 и т. Д.) И в Интернете ( 1 , 2 и т. Д.), Но ни один из соответствующих ответов не проясняет мою ситуацию.

У меня есть простой пользовательский элемент управления, состоящий из двух раскрывающихся списков. Выбор значения в одном раскрывающемся списке должен привести к заполнению другого. Пока я объявляю пользовательский элемент управления в коде .aspx, все работает как положено.

Теперь я хочу добавить пользовательский элемент управления на странице программно (по нажатию кнопки). Хотя элемент управления добавляется, выбор в одном раскрывающемся списке вызывает только обратную передачу, но не выполняет никаких действий в другом раскрывающемся списке.

Во время отладки я обнаружил, что не только OnSelectedIndexChanged не срабатывает, но также OnLoad и все другие события.

Общие причины такого поведения в отношении всех обсуждений, которые я просматривал, следующие:

  1. AutoPostBack из DropDownList не установлен в значение true или привязка данных DropDownList восстанавливается при каждой обратной передаче, что приводит к потере событий. // здесь не так, скорее всего, это динамически добавленные выпадающие списки

  2. ID назначается динамически добавляемому элементу управления автоматически при каждой обратной передаче (и каждый раз другой, так что ViewState не сохраняется правильно и события не знают, что они должны срабатывать). // хорошо, я проверил это и теперь присваиваю идентификатор вручную

  3. Элемент управления добавляется только один раз (в отличие от добавления при каждой обратной передаче, что необходимо, поскольку в ViewState сохраняются только состояние (значения) серверных элементов управления, но не сами элементы управления) и / или

  4. элемент управления добавляется при каждой обратной передаче, но слишком поздно в жизненном цикле страницы. // хорошо, теперь я добавляю свой элемент управления в OnInit обработчик событий

Чтобы сообщить странице, что элемент управления был добавлен (и сколько их), я использую Session. Ниже немного кода и, наконец, вопрос:)

.aspx:

<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
    <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</asp:Content>

и код:

protected void Page_Load(object sender, EventArgs e)
        {
            if(!this.IsPostBack)
            {
            Session.Remove("Childcontrols");
            }
        }

        private void AddTransitControl()
        {
            List<Control> controls = (List<Control>)Session["Childcontrols"];
            AddTransitPoint atp = (AddTransitPoint)LoadControl("~/UserControls/AddTransitPoint.ascx");
            string id = this.ID + "_eb" + (controls.Count).ToString();
            atp.ID = id;
            controls.Add(atp);

            PlaceHolder1.Controls.Add(atp);
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (Page.IsPostBack)
            {
                if (Session["Childcontrols"] == null)
                {
                    Session["Childcontrols"] = new List<Control>();
                }

                List<Control> controls = (List<Control>)Session["Childcontrols"];
                int count = 0;
                foreach (Control c in controls)
                {
//                    AddTransitPoint atp = (AddTransitPoint) c; //mystically not working (fires no events)
                    AddTransitPoint atp = (AddTransitPoint)LoadControl("~/UserControls/AddTransitPoint.ascx"); //it is working!

                    string id = this.ID + "_eb" + count;
                    count++;
                    atp.ID = id;
                    PlaceHolder1.Controls.Add(atp);
                }
            }
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            AddTransitControl();
        }

(Я уверен, что код самого пользовательского элемента управления не очень подходит для данного случая, но по запросу я могу добавить его позже).

Итак, теперь вопрос: методом проб и ошибок я обнаружил, что если я сохраню вновь добавленный элемент управления в коллекции в Session и OnInit, то просто вытащу элемент управления из этой коллекции и снова добавлю его в элемент управления. коллекция моего заполнителя, никакие события этого элемента управления не запускаются при следующей обратной передаче (независимо от способа вызова обратной передачи). Другой способ, если я создаю OnInit новый элемент управления для каждого, сохраненного в Session, и добавляю в коллекцию элементов управления заполнителя этот вновь созданный элемент управления - все работает! Так что же не так с сохраненными в Session элементах управления и почему они теряют свои события?

И еще один маленький вопрос здесь. Какова наилучшая практика для создания идентификаторов для таких элементов управления? Я использую строку определенного формата вместе со счетчиком, но сомневаюсь, что это лучший способ сделать это. Например, если я добавлю различные типы элементов управления, у меня возникнет проблема с этим методом.

Спасибо всем за чтение такого длинного вопроса и за ваш ценный вклад!

Ответы [ 4 ]

1 голос
/ 05 августа 2011

События элементов управления не работают при сохранении в сеансе, поскольку они теперь ссылаются на экземпляр страницы, которой больше не существует.

1 голос
/ 05 августа 2011

Мое понимание таково: события должны быть восстановлены на каждой обратной передаче. Вы получаете это бесплатно, если событие определено в файле ASPX, потому что эти атрибуты (OnClick, OnSelectionChanged и т. Д.) Повторно обрабатываются при создании экземпляра объекта страницы.

Вы не получаете это бесплатно с помощью динамически создаваемых элементов управления или элементов управления, которые определены в файле ASPX, но их события связаны в коде позади. Для этих элементов управления события не известны во время создания экземпляра, поэтому страница не может автоматически восстановить их для вас. Для этих элементов управления вы должны восстанавливать свои события при каждой обратной передаче.

Мое понимание данных, сохраняемых в сеансе, таково: когда вы помещаете объект в сеанс, сам этот объект не сохраняется в памяти. Вместо этого он сериализуется, и сериализованные данные сохраняются в сеансе. Когда вы возвращаете объект из сеанса, он десериализуется.

События объектов не сохраняют циклическую сериализацию / десериализацию, потому что они являются логически функциональными указателями - поэтому, когда объект удаляется из памяти, а затем восстанавливается, весь «ландшафт» памяти изменяется, поэтому указатели больше не будут действительными.

Я не проделал большую работу с динамически генерируемыми элементами управления, поэтому я не могу рассказать вам наиболее эффективный шаблон для управления ими и их событиями. Но я знаю, что хранение их в сеансе на самом деле не приносит вам никакой выгоды по сравнению с воссозданием их на каждой обратной передаче; и это излишне раздувает память сеанса. Надеемся, что некоторые другие SO в этой теме могут указать вам на лучший способ управления ими.

0 голосов
/ 09 июля 2013

Вам придется воссоздать состояние просмотра в элементе управления, чтобы решить проблему

0 голосов
/ 05 августа 2011

Позвольте мне перефразировать то, что, по-моему, вы говорите (после краткого взгляда на код):

Когда я динамически добавляю пользовательский элемент управления (ASCX) на страницу, элементы управления пользовательского элемента управления неfire.

Если это так, код для пользовательского элемента управления имеет значение, поскольку он должен обрабатывать события, инициируемые его элементами управления.

Если нет, то вы соединяете серверные элементы управления (созданные Microsoft или созданные вами) с делегатами для обработки события, поскольку при перетаскивании вы пропускаете шаг «Я сделаю магию для вас»элемент управления на странице.

Я хотя бы где-то близко к цели?

...