Проблема с динамическими элементами управления в .NET - PullRequest
14 голосов
/ 13 октября 2008

Проблема с динамическими элементами управления

Привет всем,

Я хочу создать некоторые динамические элементы управления, чтобы они сохраняли свое состояние просмотра при загрузке страницы. Достаточно просто, правда? Все, что мне нужно сделать, это заново создать элементы управления при каждой загрузке страницы, используя те же идентификаторы. ОДНАКО, вот подвох - в моем событии PreRender я хочу очистить коллекцию элементов управления, а затем воссоздать динамические элементы управления с новыми значениями. Причины этого сложны, и мне, вероятно, понадобится около страницы, чтобы объяснить, почему я хочу это сделать. Итак, в интересах краткости, давайте просто предположим, что я абсолютно обязан это сделать, и что другого пути нет.

Проблема возникает после того, как я заново создаю элементы управления в моем событии PreRender. Воссозданные элементы управления никогда не привязываются к состоянию просмотра, и их значения не сохраняются при загрузке страницы. Я не понимаю, почему это происходит. Я уже воссоздаю элементы управления в моем событии OnLoad. Когда я делаю это, вновь созданные элементы управления связываются с ViewState очень хорошо, при условии, что я использую одни и те же идентификаторы каждый раз. Однако, когда я пытаюсь сделать то же самое в событии PreRender, оно завершается неудачей.

В любом случае, вот мой пример кода:

namespace TestFramework.WebControls {

public class ValueLinkButton : LinkButton
{
    public string Value
    {
        get
        {
            return (string)ViewState[ID + "vlbValue"];
        }

        set
        {
            ViewState[ID + "vlbValue"] = value;
        }
    }
}

public class TestControl : WebControl
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        Controls.Clear();

        ValueLinkButton tempLink = null;

        tempLink = new ValueLinkButton();
        tempLink.ID = "valueLinkButton";
        tempLink.Click += new EventHandler(Value_Click);

        if (!Page.IsPostBack)
        {
            tempLink.Value = "old value";
        }

        Controls.Add(tempLink);
    }

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

        ValueLinkButton tempLink = ((ValueLinkButton)FindControl("valueLinkButton"));  //[CASE 1]

        //ValueLinkButton tempLink = new ValueLinkButton();  [CASE 2]

        tempLink.ID = "valueLinkButton";
        tempLink.Value = "new value";
        tempLink.Text = "Click";            

        Controls.Clear();
        Controls.Add(tempLink);
    }

    void Value_Click(object sender, EventArgs e)
    {
        Page.Response.Write("[" + ((ValueLinkButton)sender).Value + "]");
    }
}

}

Итак, давайте рассмотрим случай 1, где строка рядом с [CASE 1] не закомментирована, а строка рядом с [CASE 2] закомментирована. Здесь все работает просто отлично. Когда я помещаю этот элемент управления на страницу и загружаю страницу, я вижу ссылку с надписью «Клик». Когда я нажимаю на ссылку, на странице выводится текст «[новое значение]», а на следующей строке мы видим знакомую ссылку «Клик». Каждый раз, когда я нажимаю на ссылку «Клик», мы видим одно и то же. Пока все хорошо.

Но теперь давайте рассмотрим случай 2, где строка рядом с [CASE 1] закомментирована, а строка рядом с [CASE 2] не закомментирована. Здесь мы сталкиваемся с проблемами. Когда мы загружаем страницу, мы видим ссылку «Клик». Однако, когда я нажимаю на ссылку, страница выводит текст «[]» вместо «[новое значение]». Событие клика срабатывает нормально. Однако текст «нового значения», который я назначил атрибуту Value элемента управления, не сохраняется. Еще раз, это немного загадка для меня. Почему, когда я воссоздаю элемент управления в OnLoad, все в порядке, но когда я воссоздаю элемент управления в PreRender, значение не сохраняется?

Я чувствую, что просто должен быть способ сделать это. Когда я воссоздаю элемент управления в PreRender, есть ли способ привязать вновь созданный элемент управления к ViewState?

Я боролся с этим уже несколько дней. Будем благодарны за любую помощь, которую вы можете оказать.

Спасибо.

Ответы [ 9 ]

19 голосов
/ 13 октября 2008

Свойства, поддерживаемые ViewState, сохраняются в ViewState только в том случае, если в данный момент элемент управления отслеживает ViewState. Это сделано для того, чтобы ViewState был как можно меньше: он должен содержать только данные, которые действительно динамичны. Результатом этого является то, что:

Свойства ViewState, установленные во время события Init, не сохранены в ViewState (поскольку Страница еще не начала отслеживать ViewState). Таким образом, Init - хорошее место для добавления элементов управления и установки (a) свойств, которые не будут меняться между постбэками (ID, CssClass ...), а также начальными значениями для динамических свойств (которые затем могут быть изменены кодом в остальной жизненный цикл страницы - Загрузка, обработчики событий, PreRender).

При динамическом добавлении элементов управления в Load или PreRender отслеживается ViewState. Затем разработчик может контролировать, какие свойства сохраняются для динамически добавленных элементов управления, следующим образом:

  • Свойства, установленные до добавления элемента управления в дерево элементов управления страницы, не сохраняются в ViewState. Обычно вы устанавливаете свойства, которые не являются динамическими (идентификатор и т. Д.), Перед добавлением элемента управления в дерево элементов управления.

  • Свойства, установленные после добавления элемента управления в дерево элементов управления страницы, сохраняются в ViewState (отслеживание ViewState включено с момента события Load до события PreRender).

В вашем случае ваш обработчик PreRender устанавливает свойства перед добавлением элемента управления в дерево элементов управления страницы. Чтобы получить желаемый результат, установите динамические свойства после добавления элемента управления в дерево элементов управления: .

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    ValueLinkButton tempLink = new ValueLinkButton(); // [CASE 2]        
    tempLink.ID = "valueLinkButton"; // Not persisted to ViewState
    Controls.Clear();
    Controls.Add(tempLink);
    tempLink.Value = "new value";  // Persisted to ViewState
    tempLink.Text = "Click";       // Persisted to ViewState
}
3 голосов
/ 13 октября 2008

Поскольку у других есть утверждение, вам нужно убедиться, что вы создаете его с помощью метода Init. Чтобы узнать больше о жизненном цикле страницы ASP.NET, прочитайте эту статью: http://msdn.microsoft.com/en-us/library/ms178472.aspx

1 голос
/ 13 октября 2008

Я уже воссоздаю элементы управления в моем событии OnLoad.

Это твоя проблема. OnLoad слишком поздно. Вместо этого используйте Init.

0 голосов
/ 16 июля 2009

Вчера я понял, что вы можете заставить ваше приложение работать как обычно, загрузив дерево элементов управления сразу после запуска loadviewstateevent. если вы переопределите событие loadviewstate, вызовите mybase.loadviewstate и затем поместите свой собственный код для регенерации элементов управления сразу после него, значения для этих элементов управления будут доступны при загрузке страницы. В одном из моих приложений я использую поле viewstate для хранения идентификатора или информации о массиве, которую можно использовать для воссоздания этих элементов управления.

Protected Overrides Sub LoadViewState(ByVal savedState As Object)
    MyBase.LoadViewState(savedState)
    If IsPostBack Then
        CreateMyControls()
    End If
End Sub
0 голосов
/ 13 октября 2008

Элемент управления добавлен до того, как метод SaveViewState, вызванный в жизненном цикле элемента управления, должен сохранить свои значения. Я согласен с ответом Джо. Проверьте это изображение

http://emanish.googlepages.com/Asp.Net2.0Lifecycle.PNG

0 голосов
/ 13 октября 2008

ViewState работает на странице и ее дочерних объектах. Новый элемент управления в [Case 2] не был добавлен на страницу (или любой из ее дочерних элементов). Фактически, в случае с приведенным выше кодом, объект выйдет из области видимости, как только завершится метод OnPreRender, и будет удален сборщик мусора.

Если вам абсолютно необходимо поменять элемент управления, вам необходимо удалить старый элемент управления из его родителя с помощью метода Remove () и добавить новый элемент управления в нужное место с помощью AddAt ().

Если бы элемент управления был единственным потомком родителя, код был бы примерно таким:

ValueLinkButton tempLink = new ValueLinkButton();
Control parent = FindControl("valueLinkButton").Parent;
parent.Remove(FindControl("valueLinkButton"));
parent.AddAt(0, tempLink);
0 голосов
/ 13 октября 2008

Попробуйте позвонить Page.RegisterRequiresControlState(). Вы также можете использовать RequiresControlState(), чтобы проверить, было ли оно уже зарегистрировано.

0 голосов
/ 13 октября 2008

Я полагаю, что после добавления динамических элементов управления на страницу в PageLoad ViewState привязывается к элементам управления, и флаг «ViewState все еще должен быть связан» (в концепции, а не фактический флаг) очищается. Затем, когда вы воссоздаете элементы управления, существующее ViewState больше не привязывается.

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

0 голосов
/ 13 октября 2008

Спасибо за вашу помощь, но я попробовал это, и это не имело никакого значения. Кроме того, OnLoad работает так же хорошо для динамических элементов управления, как OnInit, если вы каждый раз присваиваете своим элементам управления одинаковые идентификаторы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...