Дочерний элемент управления Asp Composite control (радиокнопка) теряет проверенное значение

Я работаю над викториной в asp.net с динамически создаваемыми вопросами и вариантами. Главный элемент управления - это, по сути, контейнер, в котором хранятся все вопросы. В режиме просмотра «Дизайн» пользователи могут добавлять вопросы с помощью настраиваемого редактора коллекций. Каждый раз, когда я добавляю вопрос в список редактора коллекции, он генерирует для меня вопросительный тег. Внутри каждого объекта вопроса находится метка и n объектов Option, которые наследуют элемент управления Radiobutton. Каждый из этих объектов Option, в свою очередь, представляет вариант, который пользователь может выбрать для каждого вопроса.

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

$

    public String checkQuestions()
    {


        if (questions != null)
        {

            foreach (Question question in questions)
            {

                options = question.readOptions();

                int i = 0;
                foreach (Option option in options)
                {
                    testLabel.Text = option.Checked.ToString(); // test purposes only
                }

            }



        }
       return errors;

    }

Однако как только я выбираю радиокнопку и нажимаю кнопку отправки, значение Checked всегда будет ложным для всех параметров. В основном он теряет свое проверенное значение после обратной передачи, и я просто застрял в попытке решить эту проблему. Был бы признателен, если бы кто-нибудь мог указать мне в правильном направлении.


person Jurgen Welschen    schedule 04.10.2010    source источник


Ответы (1)


На первый взгляд, я бы проверил две вещи. Во-первых, убедитесь, что вы реализуете IPostBackDataHandler . для этого необходимо реализовать два метода: LoadPostData и RaisePostDataChangedEvent. На мой взгляд, первый, вероятно, является источником вашей проблемы.

Обработка обратной передачи вручную

LoadPostData принимает строку postDataKey и NameValueCollection postCollection и возвращает bool, указывающее, изменилось ли значение в результате обратной передачи. Вам не нужно реализовывать это так, как изначально предполагалось .Net, например, я создал элемент управления, содержащий несколько переключателей (которые по причинам, которые здесь не важны, не могут быть просто RadioButtonList control), поэтому убедился, что все они названы свойством string GroupName, и проверил postCollection для этого GroupName:

public bool LoadPostData(string postDataKey,
    System.Collections.Specialized.NameValueCollection postCollection)
{
    bool oldValue = _isChecked;

    postCollection = HttpContext.Current.Request.Form; // See note below

    _isChecked = (postCollection[this.GroupName] == this.Text);

    return oldValue == _isChecked;
}

Вы заметите, что я здесь переопределяю postCollection; это потому, что postCollection содержит только подмножество HttpRequest.Form, соответствующее тому, что, по мнению ASP.Net, должно заботить ваш элемент управления. Поскольку вы также создаете здесь составной элемент управления, вы, вероятно, захотите сделать то же самое.

Не волнуйтесь, если это не сработает с первого раза; стоит пройтись по тому, что передается в этот метод в режиме отладки (или выводить данные в HttpContext.Trace, что мне часто кажется проще), чтобы понять, почему ваш код не совсем то, что вам нужно.

Небольшая оговорка

И последнее: LoadPostData вызывается только в том случае, если опубликованная форма содержит поле с именем, которое соответствует UniqueID вашего элемента управления. Поскольку ваш элемент управления является составным элементом управления, вы можете немного изменить его, например:

protected override void Render(HtmlTextWriter writer)
{
    base.Render(writer);

    writer.WriteBeginTag("input");
    writer.WriteAttribute("type", "hidden");
    writer.WriteAttribute("name", this.UniqueID);
    writer.WriteAttribute("value", "post");
    writer.Write(" />");
}

Это грязный прием, но он сработает; o)

Обработка состояния просмотра вручную

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

Чтобы обрабатывать состояние просмотра вручную, вам просто нужно переопределить два метода, которые, очевидно, называются LoadViewState и SaveViewState. Первый принимает object состояния просмотра для надувания, а другой возвращает ту же object структуру. Если вы заставляете ваше SaveViewState переопределение возвращать что-то, содержащее структуру, необходимую для сохранения всех важных свойств, которые необходимо сохранить, тогда вы просто раздуваете это снова в своем LoadViewState методе.

Вот здесь и проявляется первая хитрость. Есть определенные типы данных, которые вы должны использовать для сохранения состояния просмотра, и вы никогда не должны использовать какой-либо другой тип (потому что другие типы хранятся очень неэффективно). Типы, которые, вероятно, будут вам наиболее полезны: System.Web.UI.Pair < / a>, System.Web.UI.Triplet и наши старые друзья System.Collections.ArrayList и _ 30_. Пары и тройки просто хранят два или три значения типа object; ArrayLists - это фактически List<object>.

Я предполагаю, что в ваших обстоятельствах вы, вероятно, захотите сохранить либо (1) ArrayList логических флагов, сохраняя «проверяемость» ваших радиокнопок, либо (2) ArrayList строк или целых чисел, сохраняя идентификаторы или индекс проверенные радиокнопки.

В элементе управления, о котором я упоминал ранее, мне просто нужно было сохранить проверку и свойство Text, поэтому мои методы LoadViewState и SaveViewState выглядели так:

protected override void LoadViewState(object savedState)
{
    Pair state = savedState as Pair;

    if (state != null)
    {
        _isChecked = state.First as Nullable<bool> ?? false;
        this.Text = state.Second as string;
    }
}


protected override object SaveViewState()
{
    return new Pair(_isChecked, this.Text);
}

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

Дополнительная литература о viewstate

Есть пара очень полезных статей, на которые я делаю закладки, когда возюсь с viewstate. Первый объясняет, почему вы должны хранить только определенные типы в состоянии просмотра (например, с использованием ArrayList и Hashtable, а не List<T> и Dictionary<TKey, TValue>), а второй является хорошим подробным объяснением того, как на самом деле работает весь этот материал состояния просмотра.

  1. Не позволяйте BinaryFormatter добраться до этого!
  2. Истинное понимание ViewState

Надеюсь, все это поможет решить вашу проблему.

person Owen Blacker    schedule 05.10.2010
comment
Большое спасибо за это подробное объяснение, очень хорошо написанное! И да, я уже играл с viewstate, но теперь, когда вы мне их объяснили, они начинают обретать смысл. Похоже, мне нужно кое-что почитать, когда я вернусь домой! Еще раз спасибо! - person Jurgen Welschen; 06.10.2010
comment
Без проблем. Как я уже сказал, это не так уж и сложно, если вы знаете, как: o) - person Owen Blacker; 07.10.2010