Книга: C# для профессионалов. Том II
Поддержание состояния в специальном элементе управления
Поддержание состояния в специальном элементе управления
Каждый раз при создании элемента управления на сервере в ответ на запрос к серверу, он создается с самого начала. Это означает что любое простое поле элемента управления будет повторно инициализироваться. Чтобы элементы управления поддерживали состояние между запросами, они должны использовать ViewState
, о чем требуется помнить при создании элементов управления.
Чтобы проиллюстрировать это, добавим дополнительное свойство в элемент управления RainbowLabel
. Мы добавив метод с именем Cycle()
, который циклически перебирает доступные цвета и использует хранимое поле offset
для определения цвета первой буквы выводимой строки.
Это поле должно использовать ViewState
элемента управления, чтобы быть устойчивым между запросами. Если это не сделано и поле инициализируется в элементе управления, то все будет работать неправильно.
Здесь будет показан код для обоих случаев, чтобы увидеть ловушку, в которую очень легко попасть. Сначала мы посмотрим на код, который не может воспользоваться ViewState
:
public class RainbowLabel : System.Web.UI.WebControls.Label {
private Color[] colors = new Color[] {
Color.Red, Color.Orange, Color.Yellow,
Color.GreenYellow, Color.Blue, Color.Indigo, Color.Violet
};
private int offset = 0;
protected override void Render(HtmlTextWriter writer) {
string text = Text;
for (int pos = 0; pos < text.Length; pos++ ) {
int rgb = colors[(pos + offset) % 7].ToArgb() & 0xFFFFFF;
output.Write("<font color= '#" + rgb.ToString("X6") + "'>" + text[pos] + "</font>");
}
}
public void Cycle() {
offset = ++offset % 7;
}
}
Здесь мы инициализируем поле offset
нулем, а затем позволяем методу Cycle()
увеличивать его. Использование оператора %
гарантируем, что оно уменьшится до 0, если достигнет 7.
Чтобы протестировать это, требуется способ вызова метода Cycle()
и добавление кнопки к форме:
<form method="post" runat="server">
<PCS:RainbowLabel Runat="server" Text="Multicolored label!"
/>
<asp:Button Runat="server"
Text="Cycle colors" />
</form>
Co следующим обработчиком событий:
protected void cycleButton_Click(object sender, System.EventArgs e) {
this.rainbowLabel1.Cycle();
}
Если выполнить этот код, то окажется, что цвета изменяются при первом нажатии кнопки, но дальнейшие нажатия будут оставлять цвета без изменений.
Если этот элемент управления сохраняет себя на сервере между запросами, то он работает правильно, так как поле 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 {
private Color[] colors = new Color[] {
Color.Red, Color.Orange, Color.Yellow,
Color.GreenYellow, Color.Blue, Color.Indigo, Color.Violet
};
private int Offset;
protected override void Render(HtmlTextWriter writer) {
string text = Text;
GetOffset();
for (int pos = 0; pos < text.Length; pos++) {
int rgb = colors[(post + offset) % 7].ToArgb() & 0xFFFFFF;
writer.Write("<font color='#" + rgb.ToString("X6") + "'>" + text[pos] + "</font>");
}
}
private void GetOffset() {
try {
offset = (int)ViewState["_offset"],
} catch {
offset = 0;
}
}
public void Cycle() {
GetOffset();
offset = ++offset % 7;
ViewState["_offset"] = offset;
}
}
Теперь элемент управления позволит методу Cycle()
работать каждый раз. Обычно ViewState
используется для простых свойств, таких как свойства string
:
public string Name {
get {
return (string)ViewState["_name"];
}
set {
ViewState["_name"] = value;
}
}
Еще одно замечание об использовании ViewState
имеет отношение к элементам управления-потомкам. Если элемент управления имеет потомков и используется на странице больше одного раза, то возникает проблема, связанная с тем, что потомки будут по умолчанию сообща использовать свой ViewState
. Практически в каждом случае это не то поведение, которое требуется, и к счастью мы имеем простое решение. Выводя элемент управления-предок из INamingContainer
мы вынуждаем элементы управления-потомки использовать квалифицированное хранилище в ViewState
, так что элементы управления-потомки не будут сообща использовать свой ViewState
с аналогичными элементами управления-потомками с другим предком.
Использование этого интерфейса не требует никакой дополнительной реализации, нам нужно просто сказать, что мы его используем, как если бы это был просто маркер для интерпретации сервером ASP.NET. Мы сделаем это в следующем разделе.
- Производный элемент управления RainbowLabel
- Основные "рычаги" управления производительностью
- Категорийный менеджмент. Курс управления ассортиментом в рознице
- 1. Системы управления базами данных
- 4.8 Методы управления Fibre Channel
- 7.9 Будущее управления хранилищами по версии ассоциации SNIA: стандарты SMI
- 15.1.3. Обработка сигналов управления заданиями
- Группа управления конфигурацией ПО
- Системные вызовы управления процессорной привязкой
- Глава 2 Комбинированная система управления
- Почему я не нахожу в Панели управления описанных пунктов?
- Пять аспектов управления персоналом магазина