• Введение в ASP.NET 
  • Управление состоянием в ASP.NET
  • Формы Web ASP.NET
  • Серверные элементы управления ASP.NET
  • Палитра элементов управления
  • Пример серверного элемента управления
  • ADO.NET и связывание данных
  • Модернизация приложения заказа помещения
  • База данных
  • Соединение с базой данных
  • Модификация элемента управления календарем
  • Запись мероприятий в базу данных
  • Еще о связывании данных
  • Вывод данных с помощью шаблонов
  • Конфигурация приложения
  • Заключение
  • Глава 16

    Страницы ASP.NET

    Для новичков в мире C# и .NET может показаться странным, почему в книгу включена глава, посвященная ASP.NET. Это совершенно новый язык, не так ли? Не совсем. Фактически, как мы увидим, можно использовать C# для создания страниц ASP.NET. Но мы забегаем вперед. Прежде всего необходимо обсудить, что же такое ASP.NET.

    ASP.NET (Active Server Pages.NET — активные серверные страницы .NET), поставляется как часть платформы .NET и является технологией, которая позволяет динамически создавать документы на сервере Web, когда они запрашиваются через HTTP. Это в основном документы HTML, хотя в равной степени можно создавать, например, документы WML для использования в браузерах WAP или на самом деле что-то еще с помощью типа MIME.

    Технология ASP.NET аналогична таким технологиям, как PHP, ColdFusion и другим, но между ними имеется одно существенное различие. ASP.NET, как предполагает ее название, была создана с целью полной интеграции в платформу .NET, часть которой включает поддержку C#.

    Вполне возможно, что читатель обладает опытом работы с последней технологией компании Microsoft для получения динамической генерации содержимого — ASP. В этом случае, он должен, вероятно, знать, что программирование в этой технологии использует язык сценариев, такой как VBScript или JScript. Это работало, но некоторые вещи были затруднительны для тех программистов, которые привыкли использовать 'правильные' языки программирования, что приводило в результате к потере производительности.

    Одним из основных отличий, связанных с использованием более развитых языков программирования, является обеспечение полной серверной объектной модели для использования во время работы. ASP.NET предоставляет доступ ко всем элементам управления на странице, как к объектам в обширном окружении. Также на стороне сервера мы имеем доступ ко всем другим требуемым классам .NET, позволяя интеграцию многих полезных служб. Элементы управления, используемые на странице, функциональны, фактически можно делать все то же, что и с классами форм в Windows, что дает большую гибкость. По этой причине страницы ASP.NET, создающие содержимое HTML, часто называют формами Web.

    В этой главе мы проведем более подробное рассмотрение ASP.NET, включая то, как она работает, что мы можем с ней делать и где используется C#.

    Введение в ASP.NET 

    ASP.NET пользуется Информационным сервером Интернета (Internet Information Server, IIS) для доставки содержимого в ответ на запросы HTTP. Страницы ASP.NET находятся в файлах с расширением

    .aspx
    , и базовая архитектура выглядит следующим образом:

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

    □ Обработка инструкций для сервера

    □ Код на C#, VB.NET, JScript.NET или любом другом языке, который поддерживает платформа .NET сейчас или может поддерживать в будущем

    □ Содержимое в любой ферме, подходящей для сгенерированного ресурса, такого как HTML

    □ Встроенные серверные элементы правления ASP.NET Поэтому, фактически, можно иметь файл ASP.NET, состоящий просто из

    Hello!

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

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

    Управление состоянием в ASP.NET

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

    По сути, информация, например, о состоянии элементов управления в форме Web (данные, введенные в текстовые поля, выбор из выпадающих списков и т.д.) хранится в скрытых полях представления состояния (viewstate), которые являются частью страницы, сгенерированной сервером и переданной пользователю. Если в дальнейшем требуется серверная обработка типа пересылки данных формы, то происходит возврат (postback) этой информации на сервер. На сервере эта информация используется для повторного заполнения объектной модели страницы, позволяя нам действовать на ней, как если бы изменения были бы сделаны локально.

    Мы скоро увидим это в действии и укажем особенности.

    Формы Web ASP.NET

    Как упоминалось ранее, большая часть функциональности ASP.NET достигается с помощью форм Web. Скоро мы перейдем к этому вплотную и создадим простую форму Web, чтобы получить начальную точку для исследования этой технологии. Прежде всего, однако, мы должны взглянуть на особенности, имеющие отношение к созданию форм Web. Необходимо отметить, что многие разработчики ASP.NET используют для создания файлов просто текстовый редактор, такой как notepad. Дело облегчается тем, что можно, как отмечалось ранее, объединить весь код в одном файле. Код заключается между тегами

    <script>
    с использованием двух атрибутов в открывающем теге
    <script>
    следующим образом:

    <script language="с#" runat="server">

     // Серверный код располагается здесь

    </script>

    Атрибут

    runat="server"
    здесь является критически важным (и мы увидим его неоднократно в этой главе), так как он дает указание IIS выполнить этот код на сервере, а не посылать его клиенту, предоставляя, тем самым, доступ к богатому окружению, рассмотренному ранее. Можно поместить наши функции, обработчики событий и т.д. в серверные блоки сценариев.

    Если опустить атрибут

    runat="server"
    , мы, по сути, предоставим клиентский код, который откажет, если он использует какое-либо кодирование в серверном стиле, которое мы увидим в этой главе. Однако могут возникать ситуации, когда понадобиться предоставить клиентский код (на самом деле ASP.NET сам иногда создает некий код в зависимости от возможностей браузера и используемого кода формы Web). К сожалению, мы не можем использовать здесь C#, так как это будет требовать платформы .NET на стороне клиента, что может не всегда существовать, поэтому JScript является, вероятно, лучшей возможностью (так как он поддерживается на большом множестве клиентских браузеров). Чтобы изменить язык, мы просто изменяем значение атрибута
    language
    следующим образом:

    <script language="jscript">

     // Клиентский код расположен здесь, можно также использовать vbscript.

    </script>

    В равной степени можно создавать файлы ASP.NET в Visual Studio, что прекрасно для нас подходит, так как мы уже знакомы с этой средой для программирования C#. Однако применяемая по умолчанию настройка проекта для приложений Web в этой среде предоставляет чуть более сложную структуру, чем один файл

    .aspx
    . Но это не является для нас проблемой, так как делает вещи более логичными (более подходящими для программиста и менее для разработчика Web). На основе этого в данной главе мы будем пользоваться Visual Studio.NET для программирования ASP.NET.

    Рассмотрим пример. Создайте новый проект типа C# Type Web Application, как показано ниже:

    По умолчанию VS будет использовать расширения FrontPage для настройки приложения Web в требуемом месте, которое может быть удаленным, если сервер Web находится на другой машине. Но и для этого существует альтернативный (и более быстрый) метод, использование файловой системы через LAN (что является, конечно, невозможным, если удаленный сервер Web находится не в той же LAN, что и сервер разработки). Если первый метод отказывает, то VS будет пробовать другой.

    Независимо от используемого метода, VS поддерживает локальный кэш всех файлов проекта, причем в синхронизации с файлами на сервере Web.

    Через какое-то время Visual Studio должна создать следующее:

    □ Новое решение,

    PCSWebAppl
    , содержащее приложение Web на C# с именем
    PCSWebAppl

    □ 

    AssemblyInfo.cs
    — стандартный код для описания сборки

    □ 

    Global.asax
    — глобальная информация и события приложения (будет показано позже в этой главе)

    □ 

    PCSWebAppl.disco
    — файл, описывающий все службы Web в проекте, дающий возможность динамического обнаружения (подробности в следующей главе)

    □ 

    Web.config
    — конфигурационная информация для приложения (будет показано позже в этой главе)

    □ 

    WebForm1.aspx
    — первая страница ASP NET в приложении Web

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

    .aspx
    .

    Файлы

    .aspx
    можно просматривать двумя способами: в виде модели и в виде кода. Это аналогично тому, что используется для форм Windows, как мы видели раньше в этой книге. Начальное представление в VS является модельным представлением:

    Текст, показанный здесь по умолчанию, не является текстом, который мы увидим в приложении, это просто примечание от VS, сообщающее, какой режим компоновки выбран. Здесь используется режим GridLayout, который допускает большую гибкость в управлении позиционированием, но можно сменить его на FlowLayout, если требуется более традиционная схема позиционирования типа HTML. Мы рассмотрим это немного позже.

    Если выбрать представление HTML с помощью кнопки внизу окна компоновки, мы увидим код, созданный внутри файла

    .aspx
    :

    <%@ Page language="#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="PCSWebAppl.WebForm1" %>

    <html>

     <head>

      <meta name=vs_targetSchema content="Internet Explorer 5.0">

      <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">

      <meta name="CODE_LANGUAGE" Content="C#">

     </head>

     <body MS_POSITIONING="GridLayout">

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

      </form>

     </body>

    </html>

    Здесь элемент

    <html>
    заполнен несколькими метаданными, которые нас не касаются, и элементом
    <form>
    для размещения кода ASP.NET. Наиболее важной вещью в этом элементе является атрибут
    runat
    . Точно так же, как в блоках серверного кода, которые мы видели в начале раздела, он задан как server, и значит, обработка формы будет иметь место на сервере. Если не включить этот атрибут, то никакой серверной обработки выполняться не будет, и форма не будет ничего делать.

    Другая интересная вещь в отношении этого кода состоит в теге

    <@% Page %>
    в начале файла. Этот тег определяет характеристики страницы, которые важны для нас как разработчиков приложения Web на C#. Прежде всего здесь существует атрибут
    language
    , который определяет, что на этой странице будет использоваться C#, как мы видели раньше в блоках
    <script>
    (значение по умолчанию для приложения Web является VB.NET, хотя это может быть изменено через конфигурацию IIS). Следующие три атрибута являются необходимыми, так как код, управляющий страницей, был задан VS для размещения в отдельном файле
    WebForm1.aspx.cs
    . Этот файл, который мы сейчас рассмотрим, содержит определение класса, используемого в качестве базового для страницы форм Web. (Теперь мы начинаем видеть, как ASP.NET соединяется с насыщенной объектной моделью). В этом файле для создания HTML базовый класс будет использоваться в соединении с кодом.

    Отметим, что не все файлы .aspx требуют такой многослойной модели, можно использовать просто класс базовый формы Web в .NET в качестве базового класса для страницы, что используется по умолчанию. В этом случае файл

    .aspx
    должен включать весь код C# в блоках
    <script>
    , как упоминалось ранее.

    Так как мы предоставляем специальный базовый класс для страницы, мы будем иметь специальные события. Чтобы гарантировать, что IIS знает об этом, мы используем атрибут

    AutoEventWireup
    , который означает, что обработчик событий
    Page_Load()
    , вызываемый при загрузке страницы, связывается автоматически с событием
    OnPageLoad
    . Задавая этот атрибут как
    false
    , мы должны предоставить, если потребуется, свой собственный код для выполнения этого, что даст нам большую свободу действий.

    Теперь посмотрим на "код позади" кода, сгенерированного для этого файла. Чтобы сделать это, щелкнем правой кнопкой мыши на

    WebForm1.aspx
    в утилите анализа решения (solution explorer) и выберем View Code. Код
    WebForm1.aspx.cs
    должен загрузиться в текстовый редактор. Прежде всего можно видеть объявление пространств имен для приложения Web, за которым следует используемое по умолчанию множество ссылок, требуемое для базового использования:

    namespace PCSWebAppl {

     using System;

     using System.Collections;

     using System.ComponentModel;

     using System.Data;

     using System.Drawing;

     using System.Web;

     using System.Web.SessionState;

     using System.Web.UI;

     using System.Web.UI.WebControls;

     using System.Web.UI.HtmlCotrols;

    Двигаясь дальше, мы видим определение

    WebForm1
    — базового класса, используемого для страницы .aspx. Этот класс наследует из
    System.Web.UI.Page
    , базового класса форм Web:

     /// <summary>

     /// Краткое описание WebForm1

     /// </summary>

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

    Остальная часть кода в этой форме выполняет различные инициализационные задачи, а также включает код, требуемый для создания форм Web в VS. Конструктор регистрирует обработчика событий

    Page_Init()
    , вызываемого во время активации страницы и используемого VS для кода, связанного с созданием временных добавлений к форме (хранимых в
    InitializeComponent()
    , который вызывается этим обработчиком). Существует также обработчик событий Page_Load(), упоминавшийся ранее:

      public WebForm1() {

       Page.Init += new System.EventHandler(Page_Init)

      }


      protected void Page_Load(object sender, System.EventArgs e) {

       // Поместите здесь код пользователя для инициализации страницы

      }


      protected void Page_Init(object sender, EventArgs e) {

       //

       // CODEGEN: Этот вызов требуется для ASP.NET Windows

       // Form Designer.

       //

       InitializeComponent();

      }

    Сам метод

    InitializeComponent()
    содержится в блоке
    #region
    , поэтому мы используем схематичное представление в VS, чтобы его скрыть, поскольку он быстро заполнится сгенерированным кодом VS (так же как аналогичный ему метод в коде форм Windows):

    #region код, созданный Web Form Designer

      /// <summary>

      /// Метод, требуемый для поддержки Designer; не изменяйте

      /// содержимое этого метода с помощью редактора кода.

      /// </summary>

      private void InitializeComponent() {

       this.Load += new System.EventHandler(this.Page_Load);

      }

    #endregion

     }

    }

    Так как

    AutoEventWireup
    был задан как
    false
    , то
    InitializeComponent()
    должен зарегистрировать
    Page_Load()
    с событием
    Load
    .

    Строго говоря, этот код больше, чем требуется для простой страницы формы Web ASP.NET, которую мы уже видели (хотя и в качестве тривиального примера). Однако созданная структура приспособлена для целей повторного использования и расширения с помощью технологий C#, не требуя заметного объема накладных расходов, поэтому мы будем ее использовать.

    Серверные элементы управления ASP.NET

    Созданный нами код делает пока еще очень немногое, поэтому далее нам нужно добавить некоторое содержимое. Можно сделать это в VS, используя построитель форм Web, который поддерживает режим перетаскивания элементов и добавления через код точно так же, как построитель форм Windows.

    Существует четыре типа элементов управления, которые можно добавлять к страницам ASP.NET:

    □ Элементы управления сервера HTML — элементы управления, которые имитируют элементы HTML, известные разработчикам HTML

    □ Элементы управления сервера Web — новое множество элементов управления, некоторые из которых имеют такую же функциональность, как и элементы управления HTML, но с общей схемой имен свойств и т.д., служащей для облегчения разработки (и обеспечения согласованности с аналогичными элементами управления форм Windows); существует также несколько совершенно новых и очень мощных элементов управления, как мы увидим позже

    □ Элементы управления проверкой достоверности — множество элементов управления, способных выполнять простую проверку ввода пользователя

    □ Заказные и обычные элементы управления пользователя — элементы управления, используемые разработчиком, которые можно определить рядом способов, как будет показано в главе 18

    Мы увидим полный список элементов управления сервера Web и проверкой достоверности в следующем разделе, вместе с примечаниями по использованию. Они не будут рассматриваться в этой главе. Эти элементы управления не предоставляют ничего такого, что не делают элементы управления сервера Web, а элементы управления сервера Web дают более насыщенную среду для тех, кто больше привык к программированию, чем к проектированию HTML. (Что должно относиться к большинству аудитории этой книги). Умение использовать элементы управления сервера Web, дает возможность пользоваться элементами управления сервера HTML без всяких проблем.

    Давайте добавим пару элементов управления сервера Web в наш проект. Все элементы управления сервера Web и проверкой достоверности даются в следующей форме типа элемента XML:

    <asp:X runat="server" attribute="value">Contents</asp:X>

    Здесь

    X
    является именем элемента управления сервера ASP.NET,
    attribute="value"
    является одной или несколькими спецификациями атрибутов, a Contents определяет содержимое элемента управления, если оно существует. Некоторые элементы управления позволяют задавать свойства с помощью атрибутов и содержимого элемента управления, например,
    Label
    (используемый для вывода простого текста), где текст можно определить любым образом. Другие элементы управления могут использовать схему вложенности элементов для определения их иерархии, например,
    Table
    (определяющий таблицу), который может содержать элементы
    TableRow
    , чтобы декларативно определить строки таблицы.

    Отметим, что, так как синтаксис элементов управления основывается на XML (хотя они могут использоваться встроенными в код, не являющийся XML, такой как HTML, будет ошибкой отсутствие закрывающих тегов, отсутствие

    />
    для пустых элементов или перекрытие элементов управления.

    Наконец, мы снова видим атрибут

    runat="server"
    в элементе управления сервера Web. Он важен здесь так же, как и в других местах, и распространенной ошибкой является пропуск этого атрибута, приводящий к неработающим формам Web.

    В первом примере мы делаем все простым способом. Измените представление в виде кода HTML для

    WebForm1.aspx
    так:

    <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="PCSWebAppl.WebForm1" %>

    <html>

     <head>

      <meta name=vs_targetSchema content="Internet Explorer 5.0" >

      <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">

      <meta name="CODE_LANGUAGE" Content="C#">

     </head>

     <body MS_POSITIONING="GridLayout">

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

       <asp:Label Runat="server" ID="resultLabel"/>

       <br>

       <asp:Button Runat="server" ID="triggerButton" Text="Click Me" />

      </form>

     </body>

     </html>

    Обратите внимание, что при вводе этого кода VS пытается предсказать ввод так же, как это делается при создании кода C#.

    Здесь мы добавили два элемента управления формы Web — метку и кнопку.

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

    ID
    . Как и для форм Windows, мы имеем полный доступ к свойствам, событиям и т.д. через окно Properties, и можем видеть немедленные изменения в коде или визуальном представлении, когда производятся эти изменения.

    Теперь посмотрим еще раз на

    WebForm1.aspx.cs
    . Мы видим, что к классу
    WebForm1
    были добавлены следующие два члена:

    protected System.Web.UI.WebControls.Button triggerButton;

    protected System.Web.UI.WebControls.Label resultLabel;

    Любые добавляемые серверные элементы управления автоматически будут становиться частью объектной модели создаваемой формы.

    Чтобы приложение действительно что-то делало, давайте добавим обработчик событий для нажатия кнопки. Здесь мы можем либо ввести имя метода в окне Properties для кнопки, либо просто сделать двойной щелчок на кнопке, чтобы получить используемый по умолчанию обработчик событий. Если сделать двойной щелчок на кнопке, то автоматически добавится следующий метод обработки события:

    protectеd void triggerButton_Click(object sender, System EventArgs e) {

    }

    Он соединяется с кнопкой с помощью кода, добавляемого в

    InitializeComponent()
    :

    private void InitilizeComponent()

     this.triggerButton.Click += new System.EventHandler(this.triggerButton_Click);

     this.Load += new System.EventHandler(this.Page_Load);

    }

    Изменим код в

    triggerButton_Click()
    следующим образом:

    protected void triggerButtor_Click(object sender, System.EventArgs e) {

     resultLabel.Text = "Button clicked:";

    }

    Теперь мы готовы к запуску приложения. Строим приложение в VS обычным образом, все файлы будут откомпилированы и/или помещены на сервер Web готовыми к использованию. Чтобы протестировать приложение Web, можно либо запустить приложение (что предоставит весь набор средств отладки VS), либо просто направить браузер на адрес

    http://localhost/PCSWebAppl/webForm1.aspx
    . В любом случае можно будет увидеть кнопку Click Me на странице Web. До нажатия кнопки посмотрим на код, полученный браузером, с помощью View|Source (в IE). Раздел
    <form>
    должен выглядеть примерно следующим образом:

    <form name="ctrl1" method="post" action="webform1.aspx" id="ctrl1">

     <input type="hidden" name="_VIEWSTATE"

      value="dDwtMzQ3NzI5OTM4Ozs+0RD39htoKLKMO7Yf41cFXM2GjQU=" />

     <span id="resultLabel"></span>

     <br>

     <input type= "submit" name="triggerButton" value="Click Me" id="triggerButton" />

    </form>

    Элементы управления сервера Web сгенерировали правильный код HTML —

    <span>
    и
    <input>
    для
    <asp:Label>
    и
    <asp:Button>
    , соответственно. Также существует поле
    <input type="hidden">
    с именем
    _VIEWSTATE
    . Оно инкапсулирует состояние формы, как это упоминалось ранее. Эта информация используется, когда форма посылается назад на сервер для воссоздания UI, отслеживания изменений и т.д. Отметим, что для этого был сконфигурирован элемент
    <form>
    , он будет отправлять данные назад в
    WebForm1.aspx
    (определенное в
    action
    ) с помощью операции HTTP POST (определенной в
    method
    ). Ему было также присвоено имя
    ctrl1
    . Если посмотреть на HTML, сгенерированный более сложными формами Web, то можно увидеть, что это обычный тип присваивания и он соответствует способу, которым работает ASP.NET.

    После нажатия на кнопку и появления текста проверьте снова исходный код HTML (пробелы добавлены для ясности)

    <form name="ctrl1" method="post" асtion="WebForm1.aspx" id="ctrl1">

     <input type="hidden" name="_VIEWSTATE"

      value="dDwtMzQ3NzI5OTM4O3Q802w8MTwxPjs+ O2wbdDw7bDwxPDE+Oz47bDx0 PHA8cDxsPFR1eHQ7PjtsPEJ1dHRvbiBjbGlj a2VkITs+Pjs+Ozs+Oz4+Oz4+Oz6TChBE9Yvrgb7dL38o2VsGzc/RgA==" />

     <span id="resultLabel">Button clicked</span>

     <br>

     <input type="submit" name="triggerButton" value="Click Me" id="triggerButton" />

    </form>

    В этот раз значение

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

    Палитра элементов управления

    В этом разделе мы кратко рассмотрим доступные элементы управления, прежде чем соберем их вместе в большом и более интересном приложении. Этот раздел поделен на элементы управления сервера Web и элементы управления проверкой достоверности. Обратите внимание, что в описаниях элементов упрaвлeния ссылка идет на "свойства", во всех случаях соответствующий атрибут для использования в коде ASP.NET называется идентично. Здесь представлены только наиболее часто используемые свойства.

    Элементы управления сервера Web

    Все элементы управления сервера Web наследуются из класса

    System.Web.UI.WebControls.WebControl
    , который, в свою очередь, наследуется из класса
    System.Web.UI.Control
    . В связи с этим они обладают многими общими свойствами и событиями, которые при необходимости можно использовать. Их достаточно много, поэтому не все они будут здесь показано, также как и свойства, и события самих элементов управления сервера Web.

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

    ForeColor
    ,
    Backcolor
    ,
    Font
    и т. д. Но можно прибегнуть также к помощи классов CSS (каскадных таблиц стилей), задавая для строкового свойства
    CssClass
    имя класса CSS в отдельном файле. Другими примечательными свойствами являются
    Width
    и
    Height
    для размера элемента управления,
    AccessKey
    и
    TabIndex
    для облегчения взаимодействия пользователя, и
    Enabled
    для определения того, что функциональность элемента управления обеспечивается в форме Web.

    Из событий мы, наверно, чаще всего будем использовать унаследованное событие

    Event
    для выполнения инициализации элемента управления, и
    PreRender
    для выполнения последних модификаций перед тем, как HTML выведет элемент управления.

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

    Элемент управления Описание
    Label
    Простой вывод текста, использует свойство
    Text
    для задания и программного изменения изображаемого текста.
    TextBox
    Предоставляет текстовое окно, которое пользователи могут редактировать. Использует свойство
    Text
    для доступа к введенным данным и событие
    TextChanged
    для действия на изменениях при обратной отправке. Если требуется автоматическая обратная отправка (в противоположность использованию кнопки и т.д.), задайте свойство
    AutoPostBack
    как true.
    DropDownList
    Позволяет пользователю выбрать один вариант из списка выбора либо непосредственно из списка, либо вводя первую букву или две. Использует свойство
    Items
    для задания списка позиций (это класс
    ListItemCollection
    , содержащий объекты
    ListItem
    ) и свойства
    SelectedItem
    и
    SelectedIndex
    для определения того, что выбрано. Событие
    SelectedIndexChanged
    может использоваться для выяснения, изменился ли выбор, и этот элемент управления имеет также свойство
    AutoPostBack
    , чтобы это изменение выбора включало операцию обратной пересылки.
    ListBox
    Позволяет пользователю выбрать один или несколько элементов из списка. Задайте
    SelectionMode
    как
    Multiple
    или
    Single
    , чтобы определить, сколько элементов можно выбрать одновременно, и
    Rows
    , чтобы определить, сколько элементов показывать. Другие свойства и события такие же, как и у
    DropDownList
    .
    Image
    Выводит изображение. Используйте
    ImageUrl
    для ссылки на изображение, и
    AlternateText
    для вывода текста, если изображение не может загрузиться.
    AdRotator
    Выводит несколько изображений по очереди с выводом различных изображений после каждого обращения к серверу. Используйте свойство
    AdvertisementFile
    для определения файла XML, описывающего возможные изображения (подробности можно найти в MSDN) и событие
    ADCreated
    для выполнения обработки, прежде чем каждое изображение посылается назад. Можно также использовать свойство
    Target
    для указания открываемого окна, когда происходит щелчок мышью на изображении.
    CheckBox
    Выводит флажок, который может быть установлен или не установлен. Состояние хранится в логическом свойстве
    Checked
    , а текст, связанный с полем флажка — в свойстве
    Text
    . Свойство
    AutoPostBack
    может использоваться для инициирования автоматической обратной отправки, а событие
    CheckedChanged
    для действия при изменениях.
    CheckBoxList
    Создает группу полей флажков. Свойства и события идентичны другим элементам управления списков, таким как
    DropDownList
    .
    RadioButton
    Выводит кнопку, которая может быть включена или выключена. Обычно они группируются, так что только одна кнопка в группе может быть активной, используйте свойство
    GroupName
    для соединения элементов управления
    RadioButton
    в группу. Другие свойства и события, как в элементе управления
    CheckBox
    .
    RadioButtonList
    Создает группу переключателей, где только одна кнопка в группе может быть выбрана в данный момент времени. Свойства и события — как в других элементах управления списками.
    Calendar
    Позволяет пользователю выбрать дату на графическом изображении календаря. Этот элемент управления имеет множество свойств, имеющих отношение к стилю, но основная функциональность может быть получена с помощью свойств
    SelectedDate
    и
    VisibleDate
    (типа
    System.DateTime
    ), чтобы получить доступ к дате, выбранной пользователем и месяцу для вывода (который всегда будет содержать
    VisibleDate
    ). Ключевым событием для привязки является
    SelectionChanged
    . Обратная отправка из этого элемента управления выполняется автоматически.
    Button
    Стандартная кнопка для нажатия пользователем. Использует свойство
    Text
    для текста и событие
    Click
    для ответа на нажатие (обратная отправка на сервер выполняется автоматически). Может также использовать событие
    Command
    для ответа на последовательные нажатия, что дает при получении доступ к дополнительным свойствам
    CommandName
    и
    CommandArgument
    .
    LinkButton
    Идентичен
    Button
    , но выводит кнопку как гиперссылку.
    ImageButton
    Выводит изображение, которое служит в качестве кнопки для нажатия. Свойства и события наследуются из
    Button
    и
    Image
    .
    HyperLink
    Гиперссылка HTML. Задает место назначения с помощью
    NavigateUrl
    и текст для вывода с помощью свойства
    Text
    . Может также использовать
    ImageUrl
    в качестве ссылки для определения изображения для вывода и
    Target
    для определения используемого окна браузера. Этот элемент управления не имеет нестандартных событий, поэтому используйте вместо него
    LinkButton
    , если потребовалась дополнительная обработка при следовании по ссылке.
    Table
    Определяет таблицу. Во время проектирования применяйте его в соединении с
    TableRow
    и
    TableCell
    или программным путем присваивайте строки с помощью свойства
    Rows
    , типа
    TableRowCollection
    . Это свойство можно также использовать для изменений во время выполнения. Этот элемент управления имеет несколько свойств для стилей, специфических для таблиц, таких же, как в
    TableRow
    и
    TableCell
    .
    TableRow
    Определяет строку внутри
    Table
    . Ключевым свойством является
    Cells
    , которое является классом
    TableCellCollection
    , содержащим объекты
    TableCell
    .
    TableCell
    Определяет отдельную ячейку внутри
    TableRow
    . Используйте свойство
    Text
    для задания текста для вывода,
    Wrap
    — для определения, нужно ли сворачивать текст, и
    RowSpan
    и
    ColumnSpan
    для определения, какую часть таблицы занимает ячейка.
    Panel
    Контейнер для других элементов управления. Можно использовать
    HorizontalAlign
    и
    Wrap
    для определения того, как организуется содержимое.
    Repeater
    Используется для вывода данных из запроса данных, предоставляя большую гибкость с помощью шаблонов. Мы подробно рассмотрим этот элемент управления позже в этой главе.
    DataList
    Аналогичен элементу управления
    Repeater
    , но имеет больше гибкости, когда необходимо организовать и отформатировать данные. Может, например, автоматически вывести таблицу, которую можно будет редактировать. Его мы также будем рассматривать позднее.
    DataGrid
    Аналогичен
    Repeater
    и
    DataList
    с несколькими дополнительными возможностями, такими, как сортировка. Подробнее будет рассмотрен позже.
    Элементы управления проверкой достоверности

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

    isValid
    . Если это свойство будет
    false
    , то ввод пользователя для элемента проверки достоверности не получил подтверждение. Страница, содержащая все элементы управления, также имеет свойство
    isValid
    : если у какого-либо из элементов управления проверкой достоверности свойство
    isValid
    задано как
    false
    , то это свойство страницы также будет иметь значение
    false
    . Это свойство можно проверять из серверного кода и действовать в соответствии с ним.

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

    ErrorMessage
    какое угодно текстовое значение, то пользователь увидит его, когда попытается отправить назад неверные данные.

    Хранящийся в

    ErrorMessage
    текст можно вывести в том месте, где расположен элемент управления проверкой достоверности или в другом месте с сообщениями обо всех других элементах управления проверкой достоверности на странице. Такое поведение достигается с помощью элемента управления
    ValidationSummary
    , который выводит, если потребуется, все сообщения об ошибках вместе с дополнительным текстом.

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

    Все элементы управления проверкой наследуют из класса

    BaseValidator
    , и поэтому обладают некоторыми общими важными свойствами. Возможно, наиболее важным является рассмотренное выше свойство
    ErrorMessage
    и в этом случае свойство
    ControlToValidate
    можно считать вторым по важности. Это свойство определяет идентификатор (ID) элемента управления, который проверяется. Другим важным свойством является
    Display
    , которое определяет, поместить ли текстовое сообщение в итоговой позиции проверки (если задано как none) или в позиции проверяющего элемента. Имеется также возможность оставить место для сообщения об ошибке, даже когда оно не выводится (задавая
    Display
    как
    Static
    ) или динамически выделять место, когда потребуется, что может слегка сдвигать содержимое страницы (задавая
    Display
    как
    Dynamic
    ).

    Мы скоро рассмотрим пример, но сначала кратко опишем различные элементы управления проверкой:

    Элемент управления Описание
    RequiredFieldValidator
    Используется для проверки, ввел ли пользователь данные в элемент управления, такой как
    TextBox
    .
    CompareValidator
    Используется для проверки того, что введенные данные удовлетворяют простым требованиям, происходит сравнение с оператором
    set
    , использующим свойство
    Operator
    и свойство
    ValueToCompare
    .
    Operator
    может быть одним из
    Equal
    ,
    GreaterThan
    ,
    GraterThenEqual
    ,
    LessThen
    ,
    LessThenEqual
    ,
    NotEqual
    или
    DataTypeCheck
    . Последний из них просто сравнивает тип данных ValueToCompare с данными в проверяемом элементе управления (
    ValueToCompare
    является строковым свойством, но интерпретируется как другой тип данных на основе своего содержимого).
    RangeValidator
    Проверяет, что данные в элементе управления для проверки находятся между значениями свойств
    MaximumValue
    и
    MinimumValue
    .
    RegularExpressionValidator
    Проверяет содержимое поля на основе регулярного выражения, хранящегося в
    ValidationExpression
    . Это может быть полезно для известной последовательности, такой как zip-коды, телефонные номера, IP-номера и т.д.
    CustomValidator
    Применяется для проверки данных в элементе управления с помощью специальной функции.
    ClientValidationFunction
    используется для определения клиентской функции, используемой для проверки элемента управления (это означает, к сожалению, что мы не можем использовать C#). Эта функция должна возвращать логическое значение, указывающее, была проверка успешной или нет. Альтернативно можно взять событие
    ServerValidate
    для определения серверной функции, используемой для проверки. Эта функция является обработчиком событий с булевым типом, которая получает строку, содержащую данные для проверки вместо параметра
    EventArgs
    . Мы возвращаем
    true
    , если проверка проходит успешно, иначе
    false
    .

    Пример серверного элемента управления

    Теперь мы разберем простой пример — пришло время взглянуть на более сложный сценарий. Давайте создадим каркас приложения Web — утилиту для заказа помещения для собраний. В данный момент он будет содержать только внешний интерфейс и простую обработку событий, позже мы расширим его с помощью ADO.NET и связывания данных, чтобы включить серверную бизнес-логику.

    Форма Web, которую мы собираемся создать, будет содержать поля для имени пользователя, имени события, помещения для собраний и служителей вместе с календарем для выбора даты (предполагается в этом примере, что события продолжаются в течение полного дня). Используем элементы управления проверкой для всех полей, за исключением календаря, который будет проверяться на сервере и предоставлять дату по умолчанию в случае, если ничего не было введено.

    Для тестирования интерфейса пользователя мы будем также иметь на форме элемент управления

    Label
    , который можно использовать для вывода представляемых результатов.

    Вначале создадим в Visual Studio новый проект приложения Web с именем

    PCSWebApp2
    . Затем мы создаем форму, которая генерируется с помощью следующего кода в
    WebForm1.aspx
    (код, генерируемый автоматически, не выделен):

    <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="PCSWebApp2.WebForm1" %>

    <html>

     <head>

      <meta content=False name=vs_showGrid>

      <meta content="Internet Explorer 5.0" name="vs_targetSchema>

      <meta content="Microsoft Visual Studio 7.0" name=GENERATOR>

      <meta content=C# name=CODE_LANGUAGE>

     </head>

     <body>

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

       <h1 align="center">Enter details and set a day to initiate an event.</h1>

       <br>

       <table borderColor="#000000" cellSpacing="0" cellPadding="8"

        rules="none" align="center" bgColor="#fff99e" border="2" width="540">

        <tr>

         <td vAlign="top">Your Name:</td>

         <td vAlign="top">

          <asp:textbox id="nameBox" runat="server" width="160px" />

          <asp:requiredfieldvalidator id=validateName Runat="server"

           errormessage="You must enter a name." ControlToValidate="nameBox" display="None" />

         </td>

         <td vAlign="center" rowSpan="4" >

          <asp:calendar id="calendar" runat="server" BackColor="White" />

         </td>

        </tr>

        <tr>

         <td vAlign="top">Event Name:</td>

         <td vAlign="top">

          <asp:textbox id="eventBox" runat="server" width="160px" />

          <asp:requiredfieldvalidator id="validateEvent" Runat="server"

           errormessage="You must enter an event name."

           ControlToValidate="eventBox" display="None" />

         </td>

        </tr>

        <tr>

         <td vAlign="top">Meeting Room:</td>

         <td vAlign="top">

          <asp:dropdownlistid="roomList" runat="server" width="160px">

           <asp:ListItem Value="1">The Happy Room</asp:ListItem>

           <asp:ListItem Value="2">The Angry Room</asp:ListItem>

           <asp:ListItem Value="3">The Depressing Room</aspListItem>

           <asp:ListItem Value="4">The Funked Out Room</asp:ListItem>

          </asp:dropdownlist>

          <asp:requiredfieldvalidator id="validateRoom" Runat="server"

           errormessage="You must select a room."

           ControlToValidate="roomList" display="None" />

         </td>

        </tr>

        <tr>

         <td vAlign= " top">Attendees: </td>

         <td vAlign="top">

          <asp:listbox id="attendeeList" runat="server" width="60px"

           selectionmode="Multiple" rows="6">

           <asp:ListItem Value="1">Bill Gates</asp:ListItem>

           <asp:ListItem Value="2">Monika Lewinsky</asp:ListItem>

           <asp:ListItem Value="3">Vincent Price</asp:ListItem>

           <asp:ListItem Value="4">Vlad the Impaler</asp:ListItem>

           <asp:ListItem Value="5">Iggy Pop</asp:ListItem>

           <asp:Listltem Value="6">William Shakespeare</asp:ListItem>

          </asp:listbox>

          <asp:requiredfieldvalidator id="validateAttendees" Runat="server"

           errormessage="You must have at least one attendee."

           ControlToValidate="attendeeList" display="None" />

         </td>

        </tr>

        <tr>

         <td align="middle" colSpan="3">

          <asp:button id="submitButton" runat="server" width="100%"

           Text="Submit meeting room request" />

         </td>

        </tr>

        <tr>

         <td align="middle" colSpan="3">

          <asp:validationsummary id="validationSummary" Runat="server"

           headertext="Before submitting your request:" />

         </td>

        </tr>

       </table>

       <br> Results:

       <asp:Label Runat="server" ID="resultLabel" Text="None." />

      </form>

     </body>

    </html>

    После заголовка страницы, который записан между тегами HTML

    <h1>
    , чтобы сделать его крупным текстом в стиле заголовка, основное тело формы помещается между тегами HTML
    <table>
    . Мы могли бы использовать управляющий элемент таблицы сервера Web, но это внесло бы ненужную сложность, так как таблица используется только для форматирования вывода, а не как динамический элемент интерфейса пользователя. Таблица делится на три столбца, первый из которых содержит простые текстовые метки, второй содержит поля интерфейса пользователя, соответствующие текстовым меткам (вместе с элементами управления проверкой для них), и третий, содержащий элемент управления календарем для выбора даты, которая размещается на четырех строках. Пятая строка содержит кнопку отправки, охватывающую все столбцы, и шестая строка содержит элемент управления
    validationSummary
    для вывода сообщений об ошибках, когда потребуется (все остальные элементы управления проверкой имеют атрибут
    display="none"
    , так как они будут использовать для вывода это итоговое поле). Под таблицей находится простая метка, которую можно использовать в настоящее время для вывода результатов, пока не будет добавлен доступ к базе данных.

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

    <asp:dropdownlist id="roomList" runat="server" width="160px">

     <asp:ListItem Value="1">The Happy Room</asp:ListItem>

     <asp:ListItem Value="2">The Angry Room</asp:ListItem>

     <asp:ListItem Value="3">The Depressing Room</asp:ListItem>

     <asp:ListItem Value="4">The Funked Out Room</asp:ListItem>

     </asp:dropdownlist>

    ...

    <asp:listbox id="attendeeList" runat="server" width="160px"

     selectionmode="Multiple" rows="6">

     <asp:ListItem Value="1">Bill Gates</asp:ListItem>

     <asp:ListItem Value="2">Monika Lewinsky</asp:ListItem>

     <asp:ListItem Value="3">Vincent Price</asp:ListItem>

     <asp:ListItem Value="4">Vlad the Impaler</asp:ListItem>

     <asp:ListItem Value="5">Iggy Pop</asp:ListItem>

     <asp:ListItem Value="6">William Shakespeare</asp:ListItem>

    </asp:listbox>

    Здесь объекты

    ListItem
    соединяются с двумя элементами управления сервера Web. Эти объекты не являются полноценными элементами управления сервера Web, в связи с чем нам не нужно использовать на них
    runat="server"
    . Когда страница обрабатывается, то записи
    <asp:ListItem>
    используются для создания объектов
    ListItem
    , которые добавляются к коллекции
    Items
    родительского элемента управления списком. Это облегчает нам инициализацию списков, так как не требуется писать код для этого самостоятельно (мы должны были бы создать объект
    ListItemCollection
    , добавить объекты
    ListItem
    и затем передать коллекцию элементу управления списком). Конечно, мы можем по-прежнему сделать все это программным путем, если понадобиться.

    Созданная форма выглядит следующим образом:

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

    В действительности это не совсем так. До сих пор мы не имеем никакой проверки для элемента управления календарем. Тем не менее, это просто, так как невозможно очистить выбор в этом элементе управления, и все, что мы должны сделать, это задать начальное значение. Это возможно в обработчике событий

    Page_Load()
    для создаваемой страницы:

    private void Page_Load(object sender, System.EventArgs e) {

     if (!this.IsPostBack) {

      calendar.SelectedDate = System.DateTime.Now;

     }

    }

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

    Page_Load()
    вызывается в результате операции обратной отправки, проверяя свойство страницы
    IsPostBack
    . Если выполняется обратная отправка, то это свойство будет
    true
    , и мы оставляем только выбранную дату (в конце концов, мы не хотим потерять выбор пользователя)

    Чтобы добавить обработчик нажатия кнопки, надо дважды щелкнуть мышью на кнопке и добавить следующий код:

    protected void submitButton_Click(object sender, System.EventArgs e) {

     if (this.IsValid) {

      resultLabel.Text = roomList.SelectedItem.Text + "has been booked on " +

       calendar.SelectedDate.ToLongDateString() + " by " + nameBox.Text +

       " for " + eventBox.Text + " event. ";

      foreach (ListItem attendee in attendeeList.Items) {

       if (attendee.Selected) {

        resultLabel.Text += attendee.Text + " , ";

       }

      }

      resultLabel.Text += " and " + nameBox.Text + "will be attending.";

     }

    }

    Здесь мы задаем для

    свойства
    Text элемента управления
    resultLabel
    результирующую строку, которая появится под основной таблицей. В IE результат после отправки может выглядеть следующим образом:

    Если встретятся ошибки, то вместо этого будет активизировано

    ValidationSummary
    :

    Включение обратной отправки без проверки

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

    Enabled
    ), если не нажата кнопка отправки. Однако это ведет к другой проблеме. Элемента управления проверкой способны предотвратить обратную отправку, например, когда нажата кнопка отправки, и динамически заполнить итоговую проверку на клиенте без обращения к серверу. Если отключить итоговую проверку в
    Page_Load()
    и включить ее в обработчике события нажатия кнопки, то итоговая проверка никогда не будет выводиться в браузерах, которые поддерживают клиентскую проверку (таких, как IE), так как только включение итоговой проверки будет перехватывать запросы обратной отсылки.

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

    IsValid
    .

    Для выполнения всего этого требуется следующее изменение кода. В

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

    private void Page_Load(object sender, System.EventArgs e)

     validationSummary.Enabled = false;

     foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) {

      validator.Enabled = false;

     }

     …

    }

    В методе

    submitButton_Click()
    мы сразу включаем все элементы управления проверкой, за исключением итогового, что заставляет их проверять свои соответствующие элементы управления, вызывая метод формы
    isValid()
    , и затем проверять, как и раньше, свойство
    IsValid
    . Мы добавляем также в эту проверку предложение else, которое снова включает итоговую проверку, если оказывается, что
    IsValid
    задан как
    false
    . Это предоставляет пользователю обратную связь тогда и только тогда, когда элементы управления имеют недопустимые данные и нажата кнопка отправки.

    protected void submitButton_Click(object sender, System.EventArgs e) {

     foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) {

      validator.Enabled = true;

     }

     this.Validated);

     if (this.IsValid) {

      …

     } else {

      validationSummary.Enabled = true;

     }

    }

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

    ADO.NET и связывание данных

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

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

    IEnumerable
    ,
    ICollection
    или
    IListSource
    , что включает объекты
    DataTable
    .

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

    Модернизация приложения заказа помещения

    Создадим новое приложение Web с именем

    PCSWebApp3
    и скопируем код из созданного ранее приложения
    PCSWebApp2
    . Прежде чем начать новый код, давайте рассмотрим базу данных, к которой мы будем обращаться.

    База данных

    Для этого примера используем базу данных Microsoft Access с именем

    PCSWebApp3.mdb
    , которую можно найти вместе с загружаемым кодом для этой книги. Для учета масштаба предприятия имеет смысл использовать базу данных SQL Server, но хотя используемая техника практически одинакова, но Access все же облегчает процесс тестирования. В ходе изложения будут показаны необходимые различия в коде, когда они возникнут. Представленная база данных содержит три таблицы:

    □ 

    Attendees
    содержит список возможных почетных гостей событий.

    □ 

    Rooms
    содержит список возможных помещений для событий.

    □ 

    Events
    содержит список заказанных событий.

    Attendees

    Таблица

    Attendees
    содержит следующие столбцы:

    Столбец Тип Примечания
    ID
    AutoNumber, первичный ключ Идентификационный номер почетного гостя
    Name
    Text, необходимое значение, 50 символов Имя почетного гостя
    Email Text, необязательное значение, 50 символов Адрес e-mail почетного гостя

    База данных позволяет хранить сведения о 20 почетных гостях, каждый из которых может иметь адрес e-mail. Другое приложение может автоматически посылать письмо почетным гостям после выполнения заказа. Читателям предлагается реализовать такое приложение в качестве упражнения.

    Rooms

    Таблица

    Rooms
    содержит следующие столбцы:

    Столбец Тип Примечания
    ID
    AutoNumber, первичный ключ Идентификационный номер помещения
    Room
    Text, требуемое значение, 50 символов Название помещения
    Events

    Таблица

    Events
    содержит следующие столбцы:

    Столбец Тип Примечания
    ID
    AutoNumber, первичный ключ Идентификационный номер события
    Name
    Text, требуемое значение, 255 символов Название события
    Room
    Number, требуемое значение Идентификатор помещения для события
    AttendeeList
    Memo, требуемое значение Список имен почетных гостей
    EventData
    Date/Time, требуемое значение Дата события

    Несколько событий представлены в загружаемой базе данных.

    Соединение с базой данных

    Два элемента управления, которые мы хотели бы связать с данными, —

    attendeeList
    и
    roomList
    . Чтобы сделать это, мы должны задать свойства
    DataSource
    этих элементов управления как таблицы, содержащие данные. Код должен загрузить данные в эти таблицы и выполнить соединение. Оба эти элемента управления имеют также свойства
    DataTextField
    и
    DataValueField
    , которые определяют, какие столбцы использовать для вывода элементов списка и задания свойств
    value
    , соответственно. В обоих случаях можно задать эти свойства во время проектирования как
    Name
    и
    ID
    , что будет использоваться, как только задается свойство
    DataSource
    для заполнения элементами списка элемента управления.

    Теперь мы можем сделать это в построителе форм Web. Удалите существующие записи из кода ASP.NET для этих элементов управления. Теперь что объявления будут выглядеть следующим образом:

    ...

    <asp:dropdownlist id="roomList" runat="server" width="160px" datatextfield="Room" datavaluefield="ID" / >

    ...

    <asp:listbox id="attendeeList" runat="server" width="160px" selectionmode="Multiple" rows="6" datatextfield="Name" datavaluefield=" " >

    Следующая задача состоит в создании соединения с базой данных. Существует несколько способов это сделать. Как мы видели в главе ADO.NET ранее, обычно для создания нового соединения используется окно Server Explorer. Так как мы работаем с Access, то тип провайдера для этого соединения будет Microsoft Jet 4.0 OLE DB Provider. Когда это будет задано в окне сервера, мы сможем перетащить соединение на форму Web, что добавит объект

    Data.OleDb.OleDbConnection
    к форме с именем
    oleDbConnection1
    :

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

     ...

     protected System.Data.OleDb.OleDbConnection oleDbConnection1;

    Для соединения SQL Server будет добавлен объект

    SqlClient.SqlConnection
    .

    В метод

    InitializeComponent()
    также добавится код для задания свойства
    ConnectionString
    формы
    oleDbConnection1
    , таким образом все будет готово для использования в коде.

    Мы хотим выполнить соединение данных в обработчике событий

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

    Весь наш код будет помещен между вызовами методов

    Open()
    и
    Close()
    нашего объекта соединения:

    private void Page_Load(object sender, System.EventArgs e) {

     validationSummary.Enabled = false;

     foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) {

      validator.Enabled = false;

     }

     oleDbConnection1.Open();

     if (!this.IsPostBack) {

      calendar.SelectedDate = System.DateTime.Now;

     }

     OleDbConnection1.Close();

    }

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

    Для обмена данными нам необходимо использовать несколько объектов хранения данных. Мы можем объявить их на уровне класса, чтобы мы имели к ним доступ из других функций. Нам понадобится объект

    DataSet
    для хранения информации базы данных, три объекта
    OleDb.OleDbDataAdapter
    для выполнения запросов на множестве данных и объект
    DataTable
    хранения событий для последующего доступа. Они объявляются следующим образом:

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

     ...

     protected System.Data.DataSet ds;

     protected System.Data.DataTable eventTable;

     protected System.DataOleDb.OleDbDataAdapter daAttendees;

     protected System.DataOleDb.OleDbDataAdapter daRooms;

     protected System.Data.OleDb.OleDbDataAdapter daEvents;

    Существуют версии SQL Server всех объектов OLE DB, и их использование идентично.

    Теперь для

    Page_Load()
    надо создать объект
    DataSet
    :

    private void Page_Load(object sender, System.EventArgs e) {

     ...

     oleDbConnection1.Open();

     ds = new DataSet();

    Затем мы должны присвоить объектам

    OleDbDataAdapter
    запросы и соединить их с объектом соединения:

     ds = new DataSet();

     daAttendees =

      new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Attendees", oleDbConnection1);

     daRooms =

      new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Rooms", oleDbConnection1);

     daEvents =

      new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events", oleDbConnection1);

    Затем мы выполняем запросы с помощью вызовов

    Fill()
    :

     daEvents =

      new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events", oleDbConnection1);

     daAttendees.Fill(ds, "Attendees");

     daRooms.Fill(ds, "Rooms");

     daEvents.Fill(ds, "Events");

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

    DataSource
    элементов управления соединением таблиц, с которыми мы хотим соединиться:

     daEvent s.Fill(ds, "Events");

     attendeeList.DataSource = ds.Tables["Attendees"];

     roomList.DataSource = ds.Tables["Rooms"];

    Этот код задает свойства, но само соединение данных не произойдет, пока не будет вызван метод формы

    DataBind()
    , что мы сейчас и сделаем. Но прежде чем это сделать, заполним объект
    DataTable
    данными таблицы событий:

     roomList.DataSource = ds.Tables["Rooms"];

     eventTable = ds.Tables["Events"];

    Будем соединять данные только в том случае, если нет обратной отправки, иначе происходит просто обновление данных (которые, по предположению, являются статическими в базе данных в течение выполнения запроса заказа события). Соединение данных при обратной отправке будет также стирать выбранные значения в элементах управления

    roomList
    и
    attendeeList
    . Мы могли бы сделать об этом замечание перед соединением, а затем обновить их, но проще вызвать
    DataBind()
    в существующем операторе
    if
    (причина, почему этот оператор содержался в области кода, где открыто соединение с данными):

     eventTable = ds.Tables["Events"];

     if (!this.IsPostBack) {

      calendar.SelectedDate = System.DateTime.Now;

      this.DataBind();

     }

     oleDbConnection1.Close();

    }

    Теперь выполнение приложения приведет к доступности всех данных о помещениях и приглашенных гостях из элементов управления соединением данных.

    Модификация элемента управления календарем

    Прежде чем обсуждать добавление событий к базе данных, давайте сделаем модификацию в выводе изображения календаря. Было бы неплохо выводить любой день, где была сделана предварительная заявка, другим цветом, и сделать такие дни недоступными для выбора. Это потребует модификации способа, которым даты задаются в календаре, и способа, которым выводятся ячейки дней.

    Начнем с выбора даты. Существует три момента, когда нам необходимо проверять даты заявок на мероприятия и изменять соответственно сделанный выбор: когда мы задаем начальную дату в

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

    Код, который это делает,

    getFreeDate()
    , показан ниже:

    private System.DateTime getFreeDate(System.DateTime trialDate) {

     if (eventTable.Rows.Count > 0) {

      System.DateTime=testDate;

      bool trialDateOK = false;

      while (!trialDateOK) {

       trialDateOK = true;

       foreach (System.Data.DataRow testRow in eventTable.Rows) {

        testDate = (System.DateTime)TestRow["EventDate"];

        if (testDate.Date == trialDate.Date) {

         trialDateOK = false;

         trialDate = trialDate.AddDays(1);

        }

       }

      }

     }

     return trialDate;

    }

    Этот простой код использует объект

    eventTable
    , который заполняется в
    Page_Load()
    , для извлечения данных о мероприятии. Сначала мы проверяем тривиальный случай, где не заказано никаких мероприятий, в этом случае мы можем просто подтвердить пробную дату, возвращая ее. Затем мы просматриваем даты в таблице
    Event
    , сравнивая их с пробной датой. Если мы находим совпадение, то добавляем один день к пробной дате и снова выполняем поиск.

    Извлечение даты из

    DataTable
    удивительно просто:

    testDate = (System.DateTime)testRow["EventDate"];

    Преобразование данных столбца в

    System.DateTime
    работает прекрасно.

    Итак, прежде всего используем

    getFreeDate()
    в
    Page_Load()
    . Это просто означает внесение небольшого изменения в код, который задает свойство календаря
    SelectedDate
    :

    if (!this.IsPostBack) {

     System.DateTime trialDate = System.DateTime.Now;

     calendar.SelectedDate = getFreeDate(trialDate);

     this.DataBind();

    }

    Затем нам нужно ответить на выбор даты в календаре. Чтобы сделать это, просто добавим обработчик событий для события календаря

    SelectionChanged
    и выполним сравнение даты с датами существующих мероприятий:

    protected void calendar_SelectionChanged(object sender, System.EventArgs e) {

     System.DateTime trialDate = calendar.SelectedDate;

     calendar.SelectedDate = getFreeDate(trialDate);

    }

    Этот код идентичен коду в

    Page_Load()
    .

    Третий момент, когда необходимо выполнить эту проверку, возникает при нажатии кнопки заказа. Мы вернемся к этому немного позже, так как мы должны внести несколько изменений.

    Затем мы хотим изменить цвет ячейки дня календаря, чтобы обозначить существующие мероприятия. Чтобы сделать это, необходимо добавить обработчик событий для события

    DayRender
    объекта календаря. Это событие происходит всякий раз при изображении отдельного дня и предоставляет нам доступ к объекту выводимой ячейки и дате этой ячейки с помощью свойств
    Cell
    и Date параметра
    DayRenderEventArgs
    функции обработчика. Нам нужно просто сравнить дату изображаемой ячейки с датами в объекте
    eventTable
    и покрасить ячейку с помощью свойства
    Cell.BackColor
    , если существует совпадение:

    protected void calendar_DayRender(object sender, System.Web.UI.WebControls.DayRenderEventArgs e) {

     if (eventTable.Rows.Count > 0) {

      System.DateTime testDate;

      fоreach (System.Data.DataRow testRow in eventTable.Rows) {

       testDate = (System.DateTime)testRow["EventDate"];

       if (testDate.Date == e.Day.Date) {

        e.Cell.BackColor = Color.Red;

       }

      }

     }

    }

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

    Здесь 15, 27, 28, 29 и 30 марта содержат мероприятия, а пользователь выбирает 17 марта. Теперь, после добавления логики выбора даты, невозможно выбрать день, который показан красным цветом, и если делается такая попытка, то вместо этого будет выбрана следующая дата. Например, щелчок на 28 марта в показанном выше календаре приведет к выбору 31 марта.

    Запись мероприятий в базу данных

    Обработчик событий

    submitButton_Click()
    в настоящее время собирает строку из характеристик мероприятия и выводит ее в элементе управления
    resultLabel
    . Чтобы добавить мероприятие в базу данных, нужно реформатировать созданную строку в запрос SQL
    INSERT
    и выполнить его.

    Большая часть следующего кода выглядит знакомо:

    protected void submitButton_Click(object sender, System.EventArgs e) {

     foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) {

      validator.Enabled = true;

     }

     this.Validate();

     if (this.IsValid) {

    String attendees = "";

      foreach (ListItem attendee in attendeeList.Items) {

    if (attendee.Selected) {

        attendees += attendee.Text + " (" + attendee.Value + "), ";

       }

      }

      attendees += " and " + nameBox.Text;

      String dateString =

       calender.SelectedDate.Date.Date.ToShortDateString();

      String oleDbCommand = "INSERT INTO Events (Name, Room, " +

       "AttendeeList, EventDate) VALUES ('" + eventBox.Text + "', '" +

       roomList.SelectedItem.Value + "', '" +

       attendees + "', '" + dateString + "')";

    После создания строки запроса SQL можно использовать ее для построения объекта

    OleDb.OleDbCommand
    :

      System.Data.OleDb.OleDbCommand insertCommand =

       new System.Data.OleDb.OleDbCommand(oleDbCommand, oleDbConnection1);

    После этого снова открываем соединение, которое было закрыто в

    Page_Load()
    (это снова, возможно, не самый эффективный способ реализации, но он прекрасно подходит для целей демонстрации) и выполняем запрос:

      oleDbConnection1.Open();

      int queryResult = insertCommand.ExecuteNonQuery();

    Метод

    ExecuteNonQuery()
    возвращает целое число, определяющее, сколько строк таблицы были изменены запросом. Если оно равно 1, то это означает, что вставка была успешной. Если это так, то мы помещаем сообщение об успешном выполнении в
    resultLabel
    , выполняем новый запрос для повторного заполнения
    eventTable
    и множества данных новым списком мероприятий (мы очищаем сначала множество данных, иначе события будут дублироваться) и изменяем выбор в календаре на новую, свободную, дату:

      if (queryResult == 1) {

       resultLabel.Text = "Event Added.";

       daEvents =

        new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events" oleDbConnection1);

       dsClear();

       daEvents.Fill(ds, "Events");

       eventTable = ds.Tables["Events"];

       calendar.SelectedDate =

        getFreeDate(calendar.SelectedDate.AddDays(1));

       }

    Если

    ExecuteNonQuery()
    возвращает число, отличное от единицы, то это говорит о том, что возникла проблема. В данном примере мы не будем это затрагивать, а просто выведем уведомление об отказе в
    resultLabel
    :

       else {

        resultLabel.Text = "Event not added due to DB access " + "problem.";

       }

    И в конце мы снова закрываем соединение:

       oleDbConnection1.Close();

      } else {

       validationSummary.Enabled = true;

      }

     }

    Таким образом, версия с доступом к базе данных приложения для предварительной заявки мероприятий завершена.

    Отметим, что в связи с синтаксисом запроса SQL INSERT, мы должны избегать некоторых символов в названии мероприятия, таких как апострофы " ' ", так как они будут вызывать ошибку. Было бы относительно легко реализовать специальное правило проверки, которое не разрешает пользователям применять такие символы или выполнять некоторый тип кодирования символов перед вставкой данных и после считывания данных, но код для выполнения этого не будет здесь рассмотрен.

    Еще о связывании данных

    Когда мы рассматривали ранее в этой главе доступные серверные элементы управления, мы видели три из них, которые имеют дело с выводом данных:

    DataGrid
    ,
    Repeater
    и
    DataList
    . Все они будут очень полезны при выводе данных на страницу Web, так как они автоматически выполняют многие задачи, которые иначе потребовали бы большого объема кодирования.

    Прежде всего рассмотрим простейший элемент

    DataGrid
    . В качестве примера этого элемента управления давайте добавим вывод данных мероприятия в нижней части окна приложения
    PCSWebApp3
    . Это позволяет проигнорировать соединения с базой данных, так как они уже настроены для этого приложения:

    Добавим следующие строки в нижней части

    PCSWebApp3.aspx
    :

       <br>Results:

       <asp:label id=resultLabel Runat="server" Text="None.">None.</asp:label>

       <br> <br>

       <asp:DataGrid Runat="server" ID="eventDetails1" />

      </form>

     </body>

    </HTML>

    А следующий код добавим в метод

    Page_Load()
    в
    PCSWebApp3.aspx.cs
    :

     attendeeList.DataSource = ds.Tables["Attendees"];

     roomList.DataSource = ds.Tables["Rooms"];

     eventTable = ds.Tables["Events"];

     eventDetails1.DataSource = eventTable;

     if (!this.IsPostBack) {

      calendar.SelectedDate = System.DateTime.Now;

      this.DataBind();

     } else {

      eventDetails1.DataBind();

     }

     oleDbConnection1.Close();

    }

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

    DataBind()
    для
    DataGrid
    , чтобы отразить эти изменения. Помните, что вызов
    DataBind()
    для всей форме приведет к потере выбранного помещения и почетного гостя, это честный компромисс.

    Если снова загрузить приложение в браузер Web, то можно увидеть ниже раздела данных заявки полный список мероприятий:

    ID Name Room AttendeeList EventDate
    1 My Birthday 4 Iggy Pop (5), Sean Connery (7), Albert Einstein(10), George Clooney (14), Jules Verne (18), Robin Hood (20) and Karli Watson 17.09.2001 00:00:00
    2 Dinner 1 Bill Gates (1), Monika Lewinsky (2) and Bruce Lee 05.08.2001 00:00:00
    5 Discussion of darkness 6 Vlad the Impaler (4), Darth Vader and Beelzebub 29.10.2001 00:00:00
    6 Christmas with Pals 9 Dr Frank N Futer (11), Bobby Davro (15), John F Kennedy (16), Stephen King (19) and Karli Watson 25.12.2001 00:00:00
    7 Escape 17 Monika Lewinsky (2), Stephen King (19) and Spartacus 10.05.2001 00:00:00
    8 Planetary Conquest 14 Bill Gates (1), Albert Einstein (10), Dr Frank N Furter (11), Bobby Davro (15) and Darth Vader 15.06.2001 00:00:00
    9 Homecoming Celebration 7 William Shakespeare (6), Christopher Columbus (12), Robin Hood (20) and Ulysses 22.06.2001 00:00:00
    10 Dalek Reunion Ball 12 Roger Moore (8), George Clooney (14), Bobby Davro (15) and Davros 12.06.2001 00:00:00
    11 Romantic meal for two 13 George Clooney (14) and Donna Watson 29.03.2001 00:00:00

    Мы можем сделать также еще одну модификацию в

    submitButton_Click()
    , чтобы гарантировать, что эти данные обновляются, когда добавляются новые записи:

    if (queryResult == 1) {

     resultLabel.Text = "Event Added.";

     daEvents =

      new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events", oleDbConnection1);

     ds.Clear();

     daEvents.Fill(ds, "Events");

     eventTable = ds.Tables["Events"];

     calendar.SelectedDate =

      getFreeDate(calendar.SelectedDate.AddDaysd));

     eventDetails1.DataBind();

    }

    Отметим, что мы вызываем

    DataBind()
    на
    DataGrid
    , а не на
    this
    . Это препятствует обновлению любых данных элементов управления соединения, что было бы не нужно. Все элементы управления соединением данных поддерживают этот метод, который обычно вызывается формой, если вызывается метод верхнего уровня
    (this)dataBind()
    .

    Элемент управления

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

    Вывод данных с помощью шаблонов

    Два других элемента управления выводом данных —

    Repeater
    и
    DataList
    , требуют использования шаблонов для форматирования данных для вывода. Шаблонами в смысле ASP.NET являются параметризованные разделы кода HTML, которые используются как элементы вывода в некоторых элементах управления. Они позволяют точно определить, как данные выводятся в браузере, и могут создать без существенных усилий профессионально сделанное представление.

    Существует несколько шаблонов для настройки различных аспектов поведения списка, но шаблоном, который является важным для

    Repeater
    и
    DataList
    , является шаблон
    <ItemTemplate>
    , используемый при выводе каждого элемента данных. Мы объявляем этот шаблон (и все остальные) внутри объявления элемента управления, например:

    <asp:DataList Runat="server" ... >

     <ItemTemplate>

     ...

     </ItemTemplate>

    </asp:DataList>

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

    <%# expression %>

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

    <%# DataBinder.Eval(Container.DataItem, "ColumnName") %>

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

    Полное описание доступных шаблонов дано в следующей таблице:

    Шаблон Описание
    <ItemTemplate>
    Шаблон используется для элементов списка.
    <HeaderTemplate>
    Шаблон используется для вывода перед списком.
    <FooterTemplate>
    Шаблон используется для вывода после списка.
    <SeparatorTemplate>
    Шаблон используется между элементами списка.
    <AlternatingItemTemplate>
    Шаблон для альтернативных элементов, способствует проявлению видимости.
    <SelectedItemTemplate>
    (Только
    DataList
    ) Шаблон используется для выбранных элементов в списке.
    <EditItemTemplate>
    (Только
    DataList
    ) Шаблон используется для элементов в списке, которые редактируются.

    Рассмотрим это на примере. Используем для него запрос существующих данных в

    PCSWebApp3
    .

    Пример

    Расширим таблицу вверху страницы, чтобы она содержала

    DataList
    , выводящий каждое из мероприятий, хранящихся в базе данных. Мы сделаем эти мероприятия выбираемыми, чтобы данные любого мероприятия можно было вывести, щелкая на его имени. Изменения в коде в
    PCSWebApp3
    показаны ниже:

    <tr>

     <td align=middle colSpan=3>

      <asp:validationsummary id=validationSummary Runat="server" headertext="Before submitting your request:" />

     </td>

    </tr>

     <tr>

      <td align-left colSpan=3 width="100%">

       <table cellspacing=4>

        <tr>

         <td width="40%" bgcolor="#ccffcc" >

          <asp:DataList Runat="server"

           ID="eventDetails2"

           OnSelectedIndexChanged="eventDetails2_SelectedIndexChanged" >

           <ItemTemplate>

            <asp:LinkButton Runat="server" CommandName="Select"

             forecolor="#0000ff" ID="Linkbutton1">

             <%# DataBinder.Eval(Container.DataItem, "Name") %>

            </asp:LinkButton>

            <br>

           </ItemTemplate>

           <SelectedItemTemplate>

            <b><%# DataBinder.Eval(Container.DataItem, "Name") %></b>

            <br>

           </SelectedItemTemplate>

          </asp:DataList>

         </td>

         <td valign="top">

          <asp:Label Runat="server" ID="edName"

           Font-Name="Arial" Font-Bold="True"

           Font-Italic="True" Font-Size="14"> Select an event to view details. </asp:Label>

          <br>

          <asp:Label Runat="server" ID="edDate" />

          <br>

          <asp:Label Runat="server" ID="edRoom" />

          <br>

          <asp:Label Runat="server" ID="edAttendees" />

         </td>

        </tr>

       </table>

      </td>

     </tr>

    </table>

    Здесь мы добавили новую строку таблицы, содержащую сведения с

    DataList
    в одном столбце и представленные данные в другом. Представление данных является просто четырьмя метками для свойств мероприятия, одна из которых содержит текст "Select an event to view details", когда не выбрано никакого мероприятия (ситуация при первой загрузке формы).

    DataList
    использует
    <ItemTemplate>
    и
    <SelectedItemTemplate>
    для вывода данных мероприятия. Чтобы облегчить выбор, мы инициируем команду
    Select
    внутри
    <ItemTemplate>
    , что автоматически изменяет выбор. Мы используем также для заполнения данными мероприятия событие
    OnSelectedIndexChanged
    , которое включается, когда команда
    Select
    изменяет выбор. Обработчик событий для этого показан ниже (Отметим, что нам нужно сначала выполнить метод
    DataBind()
    для обновления выбора):

    protected void eventDetails2_SelectedIndexChanged(object sender, System.EventArgs e) {

     eventDetails2.DataBind();

     DataRow selectedEventRow =

      eventTable.Rows[eventDetails2.SelectedIndex];

     edName.Text = (string)selectedEventRow["Name"];

     edDate.Text =

      "<b>Date:</b> " +

      ((DateTime)selectedEventRow["Event Date"]).ToLongDateString();

     edAttendees.Text =

      "<b>Attendees:</b> " + (string)selectedEventRow["AttendeeList"];

     DataRow selectedEventRoomRow =

      ds.Tables["Rooms"].Rows[(int)selectedEventRow["Room"] -1];

     edRoom.Text = "<b>Room:</b> " + selectedEventRoomRow["Room"];

    }

    Здесь данные в

    ds
    и
    eventTable
    используются для заполнения деталями мероприятия.

    Как и в случае

    DataGrid
    ранее, необходимо в
    Page_Load()
    задать и связать данные для
    eventDetails2
    :

    eventDetails1.DataSource = eventTable;

    eventDetails2.DataSource = eventTable;

    ...

    eventDetails1.DataBind();

    eventDetails2.DataBind();

    И заново связать в

    submitButton_Click()
    :

    eventDetails1.DataBind();

    eventDetails2.DataBind();

    Детали мероприятия представлены в таблице:

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

    Конфигурация приложения

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

    Сразу сделаем несколько замечаний о терминологии и времени жизни приложения.

    Приложение определяется как все файлы проекта, оно сконфигурировано с помощью файлов

    web.config
    . Объект
    Application
    создается, когда приложение запускается в первый раз, что происходит, когда поступает первый запрос HTTP. В это время также срабатывает событие
    Application_Start
    , обработчик событий для которого детально описывается в
    global.asax
    (вместе со всеми другими событиями, обсуждаемыми здесь), и создается пул экземпляров
    HttpApplication
    . Каждый входящий запрос получает один из этих экземпляров, который выполняет обработку запроса (это означает, что объекты
    HttpApplication
    не нуждаются в копировании при одновременном доступе, в отличие от глобального объекта
    Application
    ). Когда все экземпляры
    HttpApplication
    заканчивают свою работу срабатывает событие
    Application_End
    , и приложение прекращается, разрушая объект
    Application
    .

    Когда отдельный пользователь использует приложение Web, запускается сеанс. Как и в случае приложения, это включает создание специфического для пользователя объекта

    Session
    вместе с включением события
    Session_Start
    . В течение сеанса в отдельные запросы могут входить события
    Application_BeginRequest
    и
    Application_EndRequest
    . Это повторяется несколько раз за сеанс, когда в приложении происходит доступ к различным ресурсам. Отдельные сеансы могут прекращаться вручную или будут прерываться, если не получают больше никаких запросов. Прекращение сеанса включает событие
    Session_End
    и разрушение объекта
    Session
    .

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

    PCSWebApp3.mdb
    . Это множество записей всегда используется только для считывания данных, так как для добавления мероприятий в базу данных используется другой метод. В таких случаях можно заполнить множество записей в обработчике событий
    Application_Start
    и сделать его доступным для всех пользователей. Единственный раз, когда понадобиться обновить множество записей, возникнет, если будет добавлено событие. Это существенно повышает производительность, так как в большинстве запросов не будет требоваться доступ к базе данных.

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

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

    Заключение

    В этой главе был представлен обзор создания приложения Web с помощью ASP.NET. Мы видели, как можно использовать информацию о C#, которая была дана в этой книге совместно с элементами управления сервера Web для получения насыщенной среды разработки. Мы разработали приложение для заказа помещения для проведения мероприятий, чтобы проиллюстрировать многие из доступных технологий, таких как существующие различные серверные элементы управления и соединение данных с ADO.NET.

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

    В заключение можно сказать, что ASP.NET является новым оружием в арсенале разработчика Web: серверная обработка на данный момент является непревзойденной, а мощнейшие возможности C# и платформы .NET очень привлекательны для пользователей. 







     

    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх