Если этот элемент управления сохраняет себя на сервере между запросами, то он работает правильно, так как поле
offset
поддерживает свое состояние, не требуя специального внимания. Однако эта техника не имеет смысла для приложения Web, когда потенциально тысячи пользователей используют его одновременно. Создание отдельного экземпляра для каждого пользователя только ухудшает ситуацию.
В любом случае решение достаточно просто. Мы должны использовать свойство
ViewState
элемента управления для сохранения и извлечения данных. Нам не нужно беспокоиться о том, как это сериализуется, создается заново или о чем-то еще,
мы просто помещаем данные в свойство и извлекаем данные, уверенные, что состояние будет поддерживаться между запросами стандартными средствами ASP.NET.
Чтобы поместить поле
offset
в
ViewState
, мы используем следующий код:
ViewState["_offset"] = offset;
ViewState
состоит из пар имя-значение, и в данном случае используется имя
_offset
. Нам не нужно объявлять его где-либо, оно будет создано при первом использовании этого кода.
Аналогично для извлечения состояния используется код:
offset = (inc)ViewState["_offset"];
Если мы сделаем это, когда ничего не хранится в
ViewState
под этим именем, то будет получено значение
null
. Простейший способ справиться с такой ситуацией — использовать вызов в блоке
try
.
Собирая все вместе, сделаем следующие изменения в коде:
public class RainbowLabel : System.Web.UI.WebControls.Label {
используется для простых свойств, таких как свойства
string
:
public string Name {
get {
return (string)ViewState["_name"];
}
set {
ViewState["_name"] = value;
}
}
Еще
одно замечание об использовании
ViewState
имеет отношение к элементам управления-потомкам. Если элемент управления имеет потомков и используется на странице больше одного раза, то возникает проблема, связанная с тем, что потомки будут по умолчанию сообща использовать свой
ViewState
. Практически в каждом случае это не то поведение, которое требуется, и к счастью мы имеем простое решение. Выводя элемент управления-предок из
INamingContainer
мы вынуждаем элементы управления-потомки использовать квалифицированное хранилище в
ViewState
, так что элементы управления-потомки не будут сообща использовать свой
ViewState
с аналогичными элементами управления-потомками с другим предком.
Использование этого интерфейса не требует никакой дополнительной реализации, нам нужно просто сказать, что мы его используем, как если бы это был просто маркер для интерпретации сервером ASP.NET. Мы сделаем это в следующем разделе.
Создание композитного специального элемента управления
В качестве несложного примера композитного специального элемента управления можно объединить элемент управления из предыдущего раздела с кнопкой цикла, которая имелась на тестовой форме.
Назовем этот композитный элемент управления
RainbowControl2
и поместим его в новый файл
RainbowControl2.cs
. Этот элемент управления должен делать следующее:
□ Наследовать из
WebControl
(а не от
Label
в этот раз)
□ Поддерживать
INamingContainer
□ Иметь два поля для хранения своих элементов управления-потомков
public class RainbowLabel2 : System.Web.UI.WebControls.WebControl, INamingContainer {
private RainbowLabel rainbowLabel = new RainbowLabel;
private Button cycleButton = new Button;
Чтобы сконфигурировать композитный элемент управления, необходимо сделать так, чтобы всякий элемент управления-потомок добавлялся к коллекции
Controls
и правильно инициализировался. Мы делаем это, переопределяя метод
CreateChildControls
и помещая туда необходимый код:
protected override void CreateChildControls {
cycleButton.Text = "Cycle colors.";
cycleButton.Click += new System.EventHandler(cycleButton_Click);
Controls.Add(cycleButton);
Controls.Add(rainbowLabel);
}
Здесь мы используем только метод
Add
коллекции
Controls
, чтобы работа была корректной. Добавляем также обработчик событий для кнопки, чтобы мы могли циклически менять цвета, это достигается точно таким же образом, как и для других событий. Обработчик событий уже знаком: