Привязка данных

147

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

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

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

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

В ASP.NET большинство элементов управления (включая TextBox, LinkButton, Image и многие другие) поддерживают привязку данных с одним значением (single-value). Такая привязка позволяет связать свойство элемента управления с источником данных, но элемент управления может отображать единственное значение. Привязываемое свойство не обязательно должно отображать нечто видимое на странице. Например, можно не только привязать текст гиперссылки, установив свойство Hyperlink.Text, но также привязать свойство NavigateUrl для указания целевого назначения ссылки. Для использования привязки одного значения создаются выражения привязки данных.

Многие веб-элементы управления поддерживают привязку с многократными значениями (repeated-value), это значит, что они могут отображать наборы элементов. Элементы управления с многократными значениями включают списки и экранные таблицы (двумя примерами могут служить ListBox и GridView). Если элемент управления поддерживает такую привязку, он всегда предоставляет свойство DataSource, которое принимает объект данных. (Обычно объекты данных являются некоторого рода коллекцией, а каждый элемент в коллекции представляет запись данных.)

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

Оба типа привязки рассматриваются в последующих разделах.

Привязка с одним значением

Элементы управления, которые поддерживают привязку данных в режиме одного значения, позволяют привязать некоторые из их свойств к выражению привязки данных. Это выражение вводится в части .aspx разметки страницы (не в поле кода) и помещается между разделителями <%# и %>. Вот пример:

<%# выражение_находится_здесь %>

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

<%# EmployeeName %>

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

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

<%# GetUserName(ID) %>
<%# 1 + (2 * 20) %>
<%# "John " + "Smith" %>
<%# Request.Browser.Browser %>

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

<body>    
    <form id="form1" runat="server">
        <div>
            <asp:Image runat="server" ImageUrl='<%# FilePath %>' ID="Image1" />
            <br />
            <asp:Label runat="server" Text='<%# FilePath %>' ID="Label1" />
            <br />
            <asp:TextBox runat="server" Text='<%# GetFilePath() %>' ID="Textbox1" />
            <br />
            <asp:HyperLink runat="server" NavigateUrl='<%# LogoPath.Value %>'
                Font-Bold="True" Text="Показать изображение" ID="Hyperlink1" />
            <br />
            <input type="hidden" runat="server" id="LogoPath" value="myimg.jpg" name="LogoPath" />
            <b><%# FilePath %></b>
            <br />
            <img src="<%# GetFilePath() %>" alt="<%# GetFilePath() %>" />
        </div>
    </form>
</body>

Как видите, можно не только привязать свойство Text элемента управления Label или TextBox, но также использовать и другие свойства, подобные ImageUrl элемента управления Image, NavigateUrl элемента управления HyperLink и даже атрибуту src статического HTML-дескриптора <img>.

Можно также разместить выражение привязки в любом месте страницы, не привязывая его ни к какому свойству или атрибуту. Например, предыдущая веб-страница содержит выражение привязки между дескрипторами <b> и </b>. Во время обработки результирующий текст будет помещен на странице и отображен шрифтом с полужирным начертанием. Можно даже поместить выражение вне раздела <form> до тех пор, пока не пытаетесь туда вставить серверные элементы управления.

Выражения на этой странице примера ссылаются на свойство FilePath, функцию GetFilePath() и свойство Value скрытого поля серверной стороны, которое объявлено на той же странице. Чтобы завершить эту страницу, вы должны определить следующие ингредиенты в блоках сценариев или классе отделенного кода:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Построить выражения привязки
        this.DataBind();
    }

    protected string GetFilePath()
    {
        return "myimg.jpg";
    }

    protected string FilePath
    {
        get { return "myimg.jpg"; }
    }
}

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

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

На рисунке показан результат запуска этой страницы:

Привязка данных одного значения в различных элементах управления

Вызов this.DataBind() часто записывается как Page.DataBind() или просто DataBind(). Эти три оператора эквивалентны. Page.DataBind() работает потому, что все классы элементов управления (включая страницы) наследуют свойство Control.Page. При записи Page.DataBind() в действительности используется свойство Page текущей страницы (указывающее на нее саму), а затем вызывается DataBind() на объекте страницы.

Другие типы выражений

Выражения привязки данных всегда находятся между символами <%# и %>. В ASP.NET также имеется поддержка различного типа выражений, которые в общем называются $-выражениями, поскольку включают в себя символ $. Формально $-выражение — это последовательность кода, которую можно добавить на страницу .aspx и которая будет вычислена конструктором выражений во время визуализации страницы. Конструктор выражений обрабатывает выражение и размещает его как строковое значение в финальной HTML-разметке.

ASP.NET включает встроенный построитель выражений, который дает возможность извлекать пользовательские параметры настройки приложения и информацию строки соединения из файла web.config. Например, для извлечения параметра настройки приложения по имени appName из раздела <appSettings> файла web.config можно использовать следующее выражение:

<asp:Literal Runat="server" Text="<%$ AppSettings:appName %>" />

Между $-выражениями и выражениями привязки данных есть несколько отличий:

Первая часть $-выражения указывает имя построителя выражений. Например, выражение AppSettings:appName работает, потому что выделенный построитель выражений AppSettingsExpressionBuilder зарегистрирован для обработки выражений, которые начинаются с AppSettings. Аналогично, ASP.NET включает ResourceExpressionBuilder для вставки ресурсов и ConnectionStringExpressionBuilder — для извлечения информации о соединении из раздела <connectionStrings> файла web.config.

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

Пользовательские построители выражений

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

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

<asp:Label ID="Label1" runat="server" Text="<%$ RandomNumber:1,6 %>" />

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

Технология, которая позволяет это сделать, называется CodeDOM (Code Document Object Model — объектная модель документа с кодом) — модель для динамической генерации конструкций кода. Каждый построитель выражений включает метод по имени GetCodeExpression(), который использует CodeDOM для генерации кода, необходимого выражению. Другими словами, чтобы создать RandomNumberExpressionBuilder, понадобится реализовать метод GetCodeExpression(), который использует CodeDOM для генерации сегмента кода вычисления случайных чисел. Ясно, что это не так просто.

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Compilation;
using System.CodeDom;
using System.Web.UI;

public class RandomNumberExpressionBuilder : ExpressionBuilder
{
    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, 
        object parsedData, ExpressionBuilderContext context)
    {
        // ...
    }

    // Генератор случайных чисел
    public static string GetRandomNumber(int min, int max)
    {
        Random random = new Random();
        return random.Next(min, max).ToString();
    }
}

Преимущество такого подхода в том, что при использовании CodeDOM генерируется единственная строка кода, необходимая для вызова метода GetRandomNumber() (вместо кода для собственно генерации случайного числа).

Теперь необходимо переопределить метод GetCodeExpression(). Это метод, который ASP.NET вызывает, когда находит выражение, которое отображается на построитель выражений (во время компиляции страницы). В этой точке нужно проверить выражение на предмет отсутствия ошибок и затем сгенерировать код для вычисления результата выражения. Генерируемый код должен быть представлен в независимой от языка манере как конструируемый объект System.CodeDom.CodeExpression. Этот динамически сгенерированный фрагмент кода будет выполняться каждый раз, когда запрашивается страница.

Вот полная структура метода GetCodeExpression():

public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, 
        object parsedData, ExpressionBuilderContext context)
{
        // entry.Expression - строка с числом 
        // без префикса (например: "1,6").
        if (!entry.Expression.Contains(","))
        {
            throw new ArgumentException(" Должны быть указаны два числа, разделенные запятой");
        }
        else
        {
            // Получить два числа
            string[] numbers = entry.Expression.Split(',');

            if (numbers.Length != 2)
            {
                throw new ArgumentException("Должны быть указаны два числа");
            }
            else
            {
                int min, max;
                if (Int32.TryParse(numbers[0], out min) &&
                    Int32.TryParse(numbers[1], out max))
                {

                    // Получить ссылку на класс, имеющий метод GetRandomNumber(). 
                    // (Это класс, где данный код выполняется.)
                    CodeTypeReferenceExpression typeRef = new CodeTypeReferenceExpression(this.GetType());

                    // Определить параметры для GetRandomNumber().
                    CodeExpression[] methodParameters = new CodeExpression[2];
                    methodParameters[0] = new CodePrimitiveExpression(min);
                    methodParameters[1] = new CodePrimitiveExpression(max);

                    // Вернуть выражение привязки вызвав метод GetRandomNumber()
                    CodeMethodInvokeExpression methodCall = new CodeMethodInvokeExpression(
                        typeRef, "GetRandomNumber", methodParameters);
                    return methodCall;
                }
                else
                {
                    throw new ArgumentException("Должны использоваться допустимые целые числа");
                }

            }
        }
}

Теперь этот построитель выражений можно скопировать в папку App_Code (или компилировать его отдельно и поместить DLL-файл сборки в папку Bin). И, наконец, чтобы использовать этот построитель выражений в веб-приложении, понадобится зарегистрировать его в файле web.config и отобразить на подходящий префикс:

<configuration>
  <system.web>
    <compilation debug="true">
      <expressionBuilders>
        <add expressionPrefix="RandomNumber" type="RandomNumberExpressionBuilder"/>
      </expressionBuilders>
    </compilation>
    
    <!-- ... -->
  </system.web>
</configuration>

Теперь в коде разметки веб-формы можно применять выражения вроде <%$ RandomNumber:1,6 %>. Эти выражения автоматически обрабатываются вашим собственным построителем выражений, который генерирует код при компиляции страницы. Однако код не выполняется до тех пор, пока страница не будет запрошена. При каждом запуске страницы будет отображаться новое случайное число (входящее в нужный диапазон).

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

<%# GetRandomNumber(1,6) %>

и добавить к странице соответствующий public- или protected-метод.

Привязка с многократными значениями

Привязка с многократными значениями позволяет связать элемент управления с целым списком информации. Этот список представлен объектом данных, который является оболочкой для коллекции элементов. Это может быть коллекция пользовательских объектов (например, в ArrayList или Hashtable) или коллекция строк (вроде DataReader или Dataset).

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

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

Свойства данных для списковых элементов управления
Описание Свойство
DataSource

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

DataSourceID

Установив это свойство, вместо передачи объекта данных в коде можно связать списковый элемент управления с элементом управления источником данных. Элемент управления источником данных сгенерирует требуемый объект данных автоматически. Использовать можно либо свойство DataSource, либо DataSourceID, но не оба сразу

DataTextField

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

DataTextFormatString

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

DataValueField

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

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

На рисунке ниже показана тестовая страница, на которой присутствуют все списочные элементы управления (за исключением BulletedList, который не поддерживает выбор). В данном примере списочные элементы управления привязаны к одному и тому же объекту данных — хеш-таблице. Когда пользователь щелкает на кнопке «Получить выбранные элементы», страница отображает текущие выбранные элементы:

Привязка с многократными значениями в списковых элементах управления

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

Коллекция Hashtable работает точно так же, как коллекции ViewState, Session, Application и Cache, которые можно использовать для хранения данных. Ниже приведен код создания и привязки хеш-таблицы:

protected void Page_Load(object sender, EventArgs e)
{
        if (!Page.IsPostBack)
        {
            // Создать источник данных
            Hashtable ht = new Hashtable();
            ht.Add("Key1", "Лазанья");
            ht.Add("Key2", "Спагетти");
            ht.Add("Key3", "Пицца");

            // Установить свойство DataSource элементов управления
            Select1.DataSource = ht;
            Select2.DataSource = ht;
            Listbox1.DataSource = ht;
            DropdownList1.DataSource = ht;
            CheckList1.DataSource = ht;
            OptionList1.DataSource = ht;

            // Привязать элементы управления
            Page.DataBind();
        }
}

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

Каждая пара "ключ-значение" в хеш-таблице представлена экземпляром класса DictionaryStructure. Класс DictionaryStructure определяет два свойства: Value (действительное хранимое значение, в данном случае — строка) и Key (уникальное имя, под которым индексировано значение). Когда вы привязываете хеш-таблицу к списочному элементу управления, то в действительности привязываете группу объектов DictionaryStructure.

В рассматриваемом примере привязанные элементы управления отображают для каждого элемента свойство Value, содержащее текст. Также они отслеживают свойство Key для последующего использования. Чтобы обеспечить это, понадобится установить свойства списков DataTextField и DataValueField, как показано ниже:

<body>    
    <form id="form1" runat="server">
        <div>
            <table width="100%">
                <tr>
                    <td>
                        <select runat="server" id="Select1" size="3" datatextfield="Value" datavaluefield="Key"
                            name="Select1" />
                    </td>
                    <td>
                        <select runat="server" id="Select2" datatextfield="Value" datavaluefield="Key" name="Select2" />
                    </td>
                    <td>
                        <asp:ListBox runat="server" ID="Listbox1" Size="3" DataTextField="Value" DataValueField="Key" />
                    </td>
                    <td>
                        <asp:DropDownList runat="server" ID="DropdownList1" DataTextField="Value" DataValueField="Key" />
                    </td>
                    <td>
                        <asp:RadioButtonList runat="server" ID="OptionList1" DataTextField="Value" DataValueField="Key" />
                    </td>
                    <td>
                        <asp:CheckBoxList runat="server" ID="CheckList1" DataTextField="Value" DataValueField="Key" />
                    </td>
                </tr>
            </table>
            <asp:Button runat="server" Text="Получить выбранные элементы" ID="cmdGetSelection" OnClick="cmdGetSelection_Click" />

            <br />
            <br />
            <asp:Literal runat="server" ID="Result" EnableViewState="False" />
        </div>
    </form>
</body>

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

protected void cmdGetSelection_Click(object sender, EventArgs e)
{
        if (Select1.SelectedIndex != -1)
            Result.Text += "- Выбранный элемент в <b>Select1</b>: " + 
                Select1.Items[Select1.SelectedIndex].Text + " - " + 
                Select1.Value + "<br />";

        if (Select2.SelectedIndex != -1)
            Result.Text += "- Выбранный элемент в <b>Select2</b>: " + 
                Select2.Items[Select2.SelectedIndex].Text + " - " + 
                Select2.Value + "<br />";

        if (Listbox1.SelectedIndex != -1)
            Result.Text += "- Выбранный элемент в <b>Listbox1</b>: " + 
                Listbox1.SelectedItem.Text + " - " + 
                Listbox1.SelectedItem.Value + "<br />";

        if (DropdownList1.SelectedIndex != -1)
            Result.Text += "- Выбранный элемент в <b>DropdownList1</b>: " + 
                DropdownList1.SelectedItem.Text + " - " + 
                DropdownList1.SelectedItem.Value + "<br />";

        if (OptionList1.SelectedIndex != -1)
            Result.Text += "- Выбранный элемент в <b>OptionList1</b>: " + 
                OptionList1.SelectedItem.Text + " - " + 
                OptionList1.SelectedItem.Value + "<br />";

        if (CheckList1.SelectedIndex != -1)
        {
            Result.Text += "- Выбранный элемент в <b>CheckList1</b>: ";
            foreach (ListItem li in CheckList1.Items)
            {
                if (li.Selected)
                    Result.Text += li.Text + " - " + li.Value + " ";
            }
        }
}

Привязка к DataReader

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

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

Привязка данных с помощью DataReader

Информация в этом примере для каждой персоны включает имя и фамилию, которые хранятся в двух разных полях. К сожалению, свойство DataTextField ожидает имя только одного поля. Использовать привязку данных для соединения этих двух фрагментов данных и создания значения для DataTextField не получится. Однако эту проблему можно решить посредством простого, но эффективного трюка — использования вычисляемого столбца. Для этого понадобится просто модифицировать запрос SELECT так, чтобы он создавал вычисляемый столбец, содержащий информацию из этих двух полей. Затем этот столбец можно применять в DataTextField. Необходимая команда SQL выглядит следующим образом:

SELECT EmployeeID, FirstName + ' ' + LastName AS FullName FROM Employees

Привязанное к данным окно списка объявляется на странице так:

<asp:ListBox ID="ListBox1" runat="server" SelectionMode="Multiple" 
	DataTextField="FullName" Height="180px" />

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

protected void Page_Load(object sender, EventArgs e)
{
        if (!Page.IsPostBack)
        {
            // Создать подключение к тестовой базе Northwind
            string connectionString = 
                WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
            SqlConnection connection = new SqlConnection(connectionString);

            // Загрузить данные таблицы Employees в DataReader
            SqlCommand command = new SqlCommand(
                "SELECT EmployeeID, FirstName + ' ' + LastName AS FullName FROM Employees", 
                connection);
            try
            {
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();

                // Привязать DataReader к списку
                ListBox1.DataSource = reader;
                ListBox1.DataBind();
                reader.Close();
            }
            finally
            {
                connection.Close();
            }
        }
}

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

Последняя часть этого примера — код для определения выбранных элементов. Как и в предыдущем примере, код достаточно прост:

protected void cmdGetSelection_Click(object sender, EventArgs e)
{
        Result.Text += "<b>Выбранные элементы</b><br>";
        foreach (ListItem item in ListBox1.Items)
        {
            if (item.Selected)
                Result.Text += String.Format("<li>({0}) {1}</li>", item.Value, item.Text);
        }
}

Если вместо ListBox нужно использовать DropDownList, CheckListBox или RadioButtonList, то для этого достаточно изменить объявление элемента управления. Часть кода, которая устанавливает привязку данных, останется прежней.

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

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

GridView

Табличный элемент управления общего назначения для отображения больших таблиц информации. Поддерживает выбор, редактирование, сортировку и перемещение по страницам. GridView — "тяжеловес" среди элементов управления ASP.NET. Он является потомком DataGrid из ASP.NET 1.x.

DetailsView

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

FormView

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

В дополнение к перечисленным здесь элементам управления привязку данных поддерживают еще некоторые более специализированные элементы управления ASP.NET. Сюда входят Menu, TreeView и AdRotator.

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

Как и списковые элементы управления, GridView предоставляет свойство DataSource для объектов данных и метод DataBind(), который инициирует чтение им объекта данных и отображение каждой записи. Однако вам не нужно использовать свойства, такие как DataTextField и DataValueField, потому что GridView автоматически генерирует столбцы для каждого свойства (если привязка осуществляется к пользовательскому объекту) или для каждого поля (если привязка выполнена к строке). Вот все, что нужно для базового представления:

<asp:GridView ID="grid" runat="server" AutoGenerateColumns="true" />

Формально даже не понадобится устанавливать свойство AutoGenerateColumns, т.к. true является значением по умолчанию. Теперь определим запрос, который выберет несколько столбцов из таблицы Employees и заполним GridView:

protected void Page_Load(object sender, EventArgs e)
{
	// ...
	SqlCommand command = new SqlCommand(
		"SELECT EmployeeID, FirstName, LastName, Title, City FROM Employees", connection);
	try
	{
		connection.Open();
		SqlDataReader reader = command.ExecuteReader();

		grid.DataSource = reader;
		grid.DataBind();
		reader.Close();
	}
	finally
	{
		connection.Close();
	}
}

На рисунке ниже показан элемент GridView, созданный этим кодом:

Простой элемент GridView

Разумеется, можно сделать намного больше для настройки внешнего вида GridView и применить расширенные средства вроде сортировки, листания страниц и редактирования (это будет продемонстрировано позже).

Привязка к DataView

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

И, наконец, если вы не позаботитесь о кодировании страницы с использованием обобщенных интерфейсов, таких как IDataReader, то ограничите данные только используемым в данный момент поставщиком данных, что затруднит модификацию и адаптацию кода в будущем. Для решения этих проблем можно использовать автономные объекты данных ADO.NET. Если вы заполняете автономный DataSet, то можете привязать его к одному или более элементов управления и применить критерии сортировки и фильтрации. К тому же DataSet является полностью обобщенным — независимо от того, какой поставщик данных использован для его наполнения, сам DataSet (и код привязки данных) выглядит одинаково.

Формально привязка никогда не выполняется к объекту DataSet или DataTable. Вместо этого вы привязываетесь к объекту DataView. Объект DataView представляет вид данных в специфическом объекте DataTable. Это значит, что следующий код:

grid.DataSource = dataTable;
grid.DataBind();

эквивалентен такому:

grid.DataSource = dataTable.DefaultView;
grid.DataBind();

Важно отметить, что каждый объект DataTable включает объект DataView по умолчанию, который представлен свойством DataTable.DefaultView. Этот трюк позволяет привязываться непосредственно к DataTable. И если вы это делаете, то ASP.NET на самом деле использует автоматически DataView по умолчанию. DataView по умолчанию не применяет никакой сортировки и не фильтрует записи. Если вы хотите поэкспериментировать с этими настройками, то можете либо сконфигурировать DataView по умолчанию, либо создать собственный DataView и явно привязать его.

Пройди тесты
Лучший чат для C# программистов