KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Симон Робинсон - C# для профессионалов. Том II

Симон Робинсон - C# для профессионалов. Том II

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Симон Робинсон, "C# для профессионалов. Том II" бесплатно, без регистрации.
Перейти на страницу:

□ Создание более развитых элементов управления

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

Создадим для начала простой производный элемент управления.

Производный элемент управления RainbowLabel

Для этого первого примера создадим производный элемент управления из элемента управления Label и переопределим его метод Render() для вывода многоцветного текста. Чтобы держать элементы управления примера в этой главе отдельно, создадим новые файлы исходного кода, поэтому для этого элемента управления создадим новый файл .cs с именем RainbowLabel.cs и введем в него следующий код:

namespace PCSCustomWebControls {

 using System;

 using System.Web.UI;

 using System.Web.UI.WebControls;

 using System.ComponentModel;

 using System.Drawing;


 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

  };


  protected override void Render(HtmlTextWriter output) {

   string text=Text;

   for (int pos=0; pos < text.Length; pos++) {

    int rgb = colors[pos % 7].ToArgb() & 0xFFFFFF;

    output.Write("<font color="#" + rgb.ToString("X6") + "'>" + text[pos] + "</font>");

   }

  }

 }

}

Этот класс выводится из существующего элемента управления Label (System.Web.UI.WebControls.Label) и не требует никаких дополнительных свойств, так как достаточно унаследованного свойства Text. Мы добавили новое скрытое поле — colors[], которое содержит массив цветов, циклически изменяющихся при выводе текста.

Основная функциональность элемента управления находится в Render(), который переопределен, так как мы хотим изменить вывод HTML. Здесь мы берем строку для вывода из свойства Text и выводим каждый символ цветом из массива colors[].

Чтобы протестировать этот элемент управления, необходимо добавить его к форме в PCSCustomWebControlsTestApp:

<form method="post" runat="server" ID="Form1">

 <PCS:RainbowLabel Runat="server" Text="Multicolored label!"

  ID="rainbowLabel1" />

</form>

Нам нужно также добавить подходящее объявление в код, реализующий форму (если оно не добавится автоматически):

public class WebForm1 : System.Web.UI.Page {

 protected RainbowLabel rainbowLabel1;

 ...

В результате будет получено:

Поддержание состояния в специальном элементе управления

Каждый раз при создании элемента управления на сервере в ответ на запрос к серверу, он создается с самого начала. Это означает что любое простое поле элемента управления будет повторно инициализироваться. Чтобы элементы управления поддерживали состояние между запросами, они должны использовать 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" ID="Form1">

 <PCS:RainbowLabel Runat="server" Text="Multicolored label!"

  ID="rainbowLabel1" />

 <asp:Button Runat="server" ID="cycleButton"

  Text="Cycle colors" OnClick="cycleButton_Click" />

</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. Мы сделаем это в следующем разделе.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*