Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1676 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 3:

Web-формы

Автоматические обратные отсылки

В нашем примере только кнопка Submit генерирует обратную отсылку на стороне клиента. А любые изменения в элементе TextBox1 или CheckBox1 не будут обработаны до нажатия этой кнопки (фактической отправки формы на серверную обработку). С одной стороны это хорошо, меньше ресурсов тратится на пересылку, но с другой - пользователь не получает немедленный отклик на свои действия.

В серверных элементах управления такой случай предусмотрен. Если включить свойство AutoPostBack элемента в состояние True, то после завершенных изменений в состоянии элемента автоматически броузером будет инициирована обратная отсылка. Это происходит за счет того, что элемент управления с таким установленным свойством при ответе наряду с HTML генерирует дополнительный скрытый JavaScript -код, заставляющий броузер выполнять обратную отсылку всякий раз, когда происходит изменение состояния этого элемента пользователем.

Проверим это на нашем предыдущем примере.

  • Выделите на странице Default.aspx объекты TextBox1 и CheckBox1 и в панели Properties установите для них свойство AutoPostBack в значение True. Вы видите, что в дескрипторах этих элементов сразу появился дополнительный атрибут AutoPostBack="True". Теперь при генерировании ответа клиенту эти элементы будут генерировать и JavaScript-код обратной отсылки.
  • Запустите страницу на выполнение и в появившемся окне броузера через контекстное меню выполните опцию View Source. Среди прочего HTML-кода можно увидеть и JavaScript-код обратной отсылки
    ..........................................................
    <script type="text/javascript">
    <!--
    var theForm = document.forms['form1'];
    if (!theForm) {
        theForm = document.form1;
    }
    function __doPostBack(eventTarget, eventArgument) {
        if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
            theForm.__EVENTTARGET.value = eventTarget;
            theForm.__EVENTARGUMENT.value = eventArgument;
            theForm.submit();
        }
    }
    // -->
    </script>
    ..........................................................
        <input id="CheckBox1" type="checkbox" name="CheckBox1" 
        onclick="javascript:setTimeout('__doPostBack(\'CheckBox1\',\'\')', 0)" />
    	
        <input id="TextBox1" type="text" name="TextBox1" value="Сидоров" 
        onchange="javascript:setTimeout('__doPostBack(\'TextBox1\',\'\')', 0)"
        onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" />
    ..........................................................
    Ах!, какие умные эти серверные элементы: сами сеют - сами жнут - сами песенки поют!!!
  • Поизменяйте состояние элементов в броузере. Теперь на каждые изменения серверу генерируется обратная отсылка (постинг) для обработки. Зачем ждать, как в армии, общей команды Submit...

Обратите внимание, что HTML-элементы во вкладке HTML панели Toolbox - слабосильные и автоматическую обратную отсылку не выполняют.

Но все хорошо в меру! Частые запросы к серверу перегружают сеть и замедляют работу. Да и встраиваемый в генерируемый сервером ответ JavaScript-код также увеличивает объем передаваемой броузеру информации. Поэтому слишком увлекаться механизмом автоматических обратных отсылок не стоит.

Сохранение состояния вида

Давайте опять вернемся к нашему простому примеру и выполним следующее:

  • Верните свойство AutoPostBack элементов CheckBox1 и TextBox1 опять в состояние False
  • Выделите элемент Label1 и установите его свойство EnableViewState в состояние False (по умолчанию оно установлено в значение True ). Немедленно в дескрипторе элемента появится атрибут EnableViewState="False"
  • Запустите приложение и отметьте, что цвет элемента Label1 (Текущее время:...) меняется только при первой отсылке после изменения элемента CheckBox1. Последующие отсылки восстанавливают начальный цвет.

Что здесь происходит, почему цвет метки выполняет только первое новое указание элемента CheckBox1... Все дело в протоколе HTTP и во времени жизни страницы. HTTP -протокол не хочет сохранять последнее состояние страницы, считая очередной запрос к странице первым, а сама страница существует только на время обработки запроса и затем немедленно выгружается, чтобы освободить ресурсы сервера для работы других страниц.

Наша метка Label1 обновляет цвет только тогда, когда срабатывает обработчик изменения состояния элемента CheckBox1. А это происходит только тогда, когда броузер пришлет информацию, что произошло событие изменения состояния элемента CheckBox1.

Допустим, что пользователь поставил галочку в CheckBox1. Броузер прислал это событие на страницу. ASP.NET это событие распознала, запустила наш обработчик изменения цвета и в соответствии с новым состоянием CheckBox1 (checked) выполнила код

Label1.ForeColor = System.Drawing.Color.White;
Label1.BackColor = System.Drawing.Color.Red;

Этим кодом объект Label1 будет настроен и сгенерирует броузеру соответствующий HTML-код цветной метки. Но при следующей обратной отсылке, если оставить объект CheckBox1 в том же неизменном состоянии checked, сведений о событии изменения его состояния сервер не получит. Поэтому и делать с элементом Label1 при его создании сервер ничего не будет, а сгенерирует его начальное состояние, установленное в панели Properties. Что пользователь в результате получит от сервера - начальные установки цвета Label1. У него стоит галочка цветности метки, а она присылается совсем с другим, начальным, цветом.

Чтобы наладить такую ситуацию, существует механизм сохранения состояния вида. Этот механизм встроен в каждый серверный элемент управления и по умолчанию включен. Это значит, что элемент на исходе жизни (своей и страницы - "поскольку каждая страница жизни начинается с чистого листа") копирует невосстанавливаемые параметры своего последнего состояния вида в скрытое поле, в дополнение к такой же информации других элементов, упаковывая ее для отправки броузеру. Элемент надеется, что в следующей новой жизни (при поступлении на сервер обратной отсылки) он сможет распакавать свою информацию и восстановить свое последнее состояние вида.

Некоторые элементы, если отключить у них механизм состояния вида, все равно могут готовить перед своей кончиной некоторую минимально необходимую информацию (control state), чтобы воскреснуть подобающим образом. Этот механизм мы можем увидеть в HTML-коде на броузере, запечатленный в скрытых полях, наподобие такой автоматически закодированной абры-кадабры

<input type="hidden" 
	       name="__VIEWSTATE" id="__VIEWSTATE" 
	       value="/wEPDwULLTE2ODAxODI5OTYPZBYCAgMPZBYEAgMPEA
	              8WAh4HQ2hlY2tlZGdkZGRkAgcPDxYCHgRUZXh0BRHQodC90LXRgtC6
	              0L7QsiEhIWRkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZX
	              lfXxYBBQlDaGVja0JveDEKD82+p/Oho3OWiA2YZad613t4ZA==" />

Атрибут value скрытого поля hidden имеет значение упакованного состояния элементов управления, которое вместе с ответом передается на временное хранение броузеру.

  • Восстановите механизм сохранения состояния вида элементу Label1 нашей страницы, вернув свойство EnableViewState в значение True. Проверьте работу элемента Label1, запустив страницу. Теперь все работает как надо - цветность метки сохраняется при каждом ответе сервера!

Отметьте для себя, что серверные элементы сохраняют интерфейсные значения даже при выключенном механизме сохранения состояния вида. Про несерверные (нет атрибута runat="server" ) HTML-элементы такого не скажешь. Web-элементы с вкладки Standard - все серверные.

Для примера добавьте на страницу (в любое место) из вкладки HTML элемент Input (Checkbox). Выполните страницу, измените состояние элемента и отправьте страницу на сервер. Сервер вернет отклик с первоначальным состоянием элемента. Если же превратить элемент Input (Checkbox) в серверный командой Run As Server Control, то элемент при постинге будет сохранять свое состояние.

Этапы выполнения страниц на сервере

Со стороны сервера обработка Web-формы (страницы) средой ASP.NET происходит поэтапно. На каждом этапе генерируются соответствующие события, которые наш код может перехватывать и выдавать нужный ответ. Поток обработки страницы проходит следующие этапы:

  • Инициализация структуры страницы
  • Инициализация кода пользователя
  • Проверка достоверности данных
  • Обработка событий
  • Автоматическая привязка данных
  • Очистка

Графически это можно изобразить так


Инициализация структуры страницы

На этом этапе страница загружается в память, расшифровывается информация о состоянии вида (если это обратная отсылка), запускается событие Page.Init. Но это событие редко обрабатывается разработчиком, поскольку объекты элементов управления еще не созданы, а информация о состоянии вида еще полностью не расшифрована.

Инициализация кода пользователя

На этом этапе все элементы управления уже созданы и инициализированы начальными свойствами. Запускается событие Page.Load и большинство разработчиков в обработчике этого события осуществляют программную перенастройку элементов управления. Событие Page.Load запускается всегда, независимо от того, запрашивается страница впервые или как часть обратной отсылки. Обработчик можно создать двойным щелчком на свободном месте Web-формы в представлении Design.

Для определения состояния страницы можно использовать свойство Page.IsPostBack, которое будет иметь значение false при первом запросе страницы. IsPostBack является статическим свойством класса Page. Оно всегда возвращает информацию, основанную на текущей странице. Можно также использовать свойство экземпляра как this.IsPostBack, что равносильно Page.IsPostBack.

Следует помнить, что в состоянии вида автоматически сохраняются все программно измененные свойства. Инициализация элемента управления в обработчике события Page.Load считается программным изменением, поэтому любое затрагиваемое нами значение элемента управления будет сохранено в состоянии вида. Это без нужды увеличивает размер страницы и замедляет время передачи данных.

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

В тех случаях, когда действительно легче инициализировать элемент управления в коде, лучше отключить состояние вида для этого элемента значением EnableViewState="false" и инициализировать элемент каждый раз при запуске события Page.Load независимо от того, является ли текущий запрос обратной отсылкой или поступил впервые. В качестве упражнения выполним программное восстановление состояния метки в зависимости от состояния элемента управления CheckBox1 нашего примера

  • Выделите элемент управления Label1 и через панель Properties установите для него EnableViewState="false"
  • Включите флажок CheckBox1 и многократно выполните обратную отсылку, чтобы убедиться, что состояние вида Label1 не сохраняется
  • Дополните блок скриптов страницы Default.aspx кодом явного вызова обработчика CheckBox1_CheckedChanged()
<%@ Page Language="C#" %>
	
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        Label1.Text = "Текущее время: "
            + DateTime.Now.ToLongTimeString();
        Label2.Text = TextBox1.Text + "!!!";
            
        // Восстанавливаем Label1 программно место состояния вида   
        CheckBox1_CheckedChanged(null, EventArgs.Empty); 
    }
	
    protected void CheckBox1_CheckedChanged(object sender, EventArgs e)
    {
        if (CheckBox1.Checked)
        {
            Label1.ForeColor = System.Drawing.Color.White;
            Label1.BackColor = System.Drawing.Color.Red;
        }
        else
        {
            Label1.ForeColor = System.Drawing.Color.Black;
            Label1.BackColor = System.Drawing.Color.White;
        }
    }
</script>
	
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Начальная страница</title>
</head>
<body>
    <form id="form1" runat="server">
        <div style="text-align: center">
            <asp:Label ID="Label1" runat="server" EnableViewState="false" />
            &nbsp;&nbsp;&nbsp;
            <asp:CheckBox ID="CheckBox1" runat="server"
                Text="Red" OnCheckedChanged="CheckBox1_CheckedChanged" /><br />
            Имя:
            <asp:TextBox ID="TextBox1" runat="server">Снетков</asp:TextBox><br />
            Привет дорогой товарищ (господин)
            <asp:Label ID="Label2" runat="server"></asp:Label><br />
            <br />
            <asp:Button ID="Button1" runat="server" Text="Обновить" />
        </div>
    </form>
</body>
</html>

Теперь мы видим, что элемент Label1 работает правильно, восстанавливая свое состояние программно в зависимости от состояния флажка.