DataGrid
Это очень популярный элемент управления, и неудивительно. Особенно много он применялся в ASP .NET 1.x, но теперь его функции перекрываются GridView. Тем не менее его стоит изучить, так как многие его свойства схожи со свойствами GridView. DataGrid делает очень легким представление табличной информации, которая содержится в базах данных, файлах XML или создается вручную. Достаточно создать DataGrid, установить свойство DataSource и получить готовую таблицу на странице. Формат таблицы можно менять независимо от данных. Данные можно сортировать, выбирать, редактировать.
В простейшем варианте нужно установить только свойство DataSource, его значением может быть объект, реализующий интерфейс IEnumerable, например SqlDataReader, DataTable. При этом на странице выводится таблица, где строкам соответствуют записи, а столбцам — поля.
Создадим простой XML-файл с табличной информацией. Это будут данные о лауреатах Нобелевской премии по литературе и физике. Назовите ее nobel.xml:
<?xml version="1.0" encoding="utf-8" ?> <nobel> <phisics> <phisisist> <name>Basov</name> <nationality>Russia(USSR)</nationality> </phisisist> <phisisist> <name>Rentgen</name> <nationality>Germany</nationality> </phisisist> <phisisist> <name>Bor</name> <nationality> Denmark</nationality> </phisisist> </phisics> <literature> <writer> <name>Boris Pasternak</name> <nationality>Russia</nationality> <work>"Doctor Zhivago"</work> <winningdate>1958</winningdate> </writer> <writer> <name>Romain Rollan</name> <nationality>France</nationality> <work>"Jean-Cristophe"</work> <winningdate>1915</winningdate> </writer> <writer> <name>Gabriel Garsia Marquez </name> <nationality>Columbia</nationality> <work>"100 years of solitude"</work> <winningdate>1982</winningdate> </writer> <writer> <name>George Bernard Shaw</name> <nationality>Great Britain</nationality> <work></work> <winningdate>1925</winningdate> </writer> </literature> </nobel>
Тут построена трехуровневая иерархия. Узел <nobel> должен быть прочитан в DataSet. Внутри него есть 2 узла: один с данными о физике, второй — о литературе. Каждый из них будет помещен в DataTable. Узлы <name>, <nationality>, <work>, <winningdate> вложены в <literature> и повторяются для каждого писателя. Они будут считаны в DataColumns таблицы.
Почему узел <work></work> у Шоу пустой? Как считал сам Шоу, Нобелевскую премию 1925 года ему дали за то, что в этом году он ничего не написал.
Через методы ReadXml, WriteXml DataSet может читать данные из XML-файла.
Тут построена трехуровневая иерархия. Узел <nobel> должен быть прочитан в DataSet. Внутри него есть 2 узла: один с данными о физике, второй — о литературе. Каждый из них будет помещен в DataTable. Узлы <name>, <nationality>, <work>, <winningdate> вложены в <literature> и повторяются для каждого писателя. Они будут считаны в DataColumns таблицы.
Почему узел <work></work> у Шоу пустой? Как считал сам Шоу, Нобелевскую премию 1925 года ему дали за то, что в этом году он ничего не написал.
Через методы ReadXml, WriteXml DataSet может читать данные из XML-файла.
Форма, которая читает информацию из этого файла:
<%@ Page Language="C#" Debug="true" %>
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Xml" %>
<script runat="server">
void Page_Load() { String xmlFilename = Server.MapPath("") + "\\nobel.xml"; DataSet newDataSet = new DataSet(); newDataSet.ReadXml(xmlFilename); DataTable newDataTable = newDataSet.Tables[1]; DataGrid1.DataSource = newDataTable; DataGrid1.DataBind(); }
void Page_Load() { if (!IsPostBack) bindData(); } </script>
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Data Grid Control example</title> </head> <body> <form runat="server"> <asp:DataGrid id="DataGrid1" runat="server" CellSpacing="10" BorderWidth ="0" BackColor="AliceBlue" EditItemIndex = "1"> <HeaderStyle BackColor="#AAAADD" Font-Size="20pt" Font- Bold = "True"> </HeaderStyle> <AlternatingItemStyle BackColor="#80FF80" /> </form> </body> </html>
Поменяв индекс в DataTable newDataTable = newDataSet.Tables[1] на 3, получим страницу с другими данными — лауреатов премии по литературе.
По умолчанию элемент DataGrid сам определяет количество полей в источнике данных и генерирует колонки таблицы. Это определяется свойством AutoGenerateColumns. С элементом управления DataGrid могут быть связаны не все типы данных. Поддерживаются примитивные типы, строки, DataTime и Decimal. Если в поле неподдерживаемый тип, столбец не будет создан. Если ни одного подходящего поля нет, будет выброшено исключение.
DataGrid имеет заголовок (Header), который по умолчанию виден, и нижний колонтитул (Footer). При автоматической генерации в заголовке каждого столбца выводится название поля.
Если AutoGenerateColumns установить в False, можно самим управлять колонками и определять более сложный его вид. В таком случае надо включать в DataGrid элементы BoundColumn. Некоторые свойства BoundColumn:
DataField определяет поле источника данных;DataFormatString задает формат вывода данных;ReadOnly делает поле недоступным для редактирования.
В заголовке и нижнем колонтитуле можно установить любой текст, а в заголовке — еще и картинку (HeaderText, FooterText, HeaderImageUrl).
В ячейку генерируемой DataGrid таблицы вставляется LiteralControl, текст которого берется из источника данных и форматируется в соответствии с DataFormatString. Для редактируемой строки в ячейке появляется TextBox.
Есть и другие типы колонок.
ButtonColumn отображает в каждой строке командную кнопку.
Если связать ее с полем, на кнопках будут надписи из этого поля.
EditCommandColumn показывает кнопки для редактирования.
HyperLinkColumn превращает текст в гиперссылки. Например, поле PhotoPath можно показать в такой колонке, и тогда щелчок по ссылке покажет фотографию.
TemplateColumn позволяет определить шаблон отображения, как в DataList.
При желании можно программно скрывать и показывать колонки, например:
DataGrid1.Columns[1].Visible = !(DataGrid1.Columns[1].Visible);
У элемента DataGrid есть 7 свойств, задающих стили различных его частей или типов строк. Все они имеют тип TableItemStyle. Это AlternatingItemStyle, EditItemStyle, FooterStyle, HeaderStyle, ItemStyle, PagerStyle и SelectedItemStyle. Стили образуют иерархию, то есть атрибут "Стиль", который выше в иерархии, наследует те, которые ниже, если он его не переопределяет. Порядок в ней такой:
EditItemStyle — стиль редактируемой строки;SelectedItemStyle — стиль выбранной строки;AlternatingItemStyle — стиль каждой второй строки;ItemStyle — стиль строки по умолчанию;ControlStyle — все свойства, которые влияют на внешний вид элемента, например BackColor. PagerStyle, FooterStyle, HeaderStyle тоже его наследуют.PagerStyle — стиль пейджера, то есть номеров страниц-гиперссылок, при выборе которых таблица перелистывается. Чтобы пейджер появился, должен быть установлен атрибут AllowPaging и количество записей должно быть больше PageSize. Все эти свойства удобно устанавливать с помощью PropertyBuilder.
В Visual Studio 2005 есть возможность автоформатирования, как и у DataList.
Новый вариант, без автоматической генерации колонок и со стилями:
<asp:DataGrid ID="DataGrid2" runat="server" BackColor="#FFE0C0" ShowFooter="True" AutoGenerateColumns="False" PageSize="3"> <AlternatingItemStyle BackColor="#C0FFC0" /> <ItemStyle BackColor="#FFFFC0" /> <EditItemStyle BackColor="#C0C000" Font-Size="XX-Large" /> <Columns> <asp:BoundColumn DataField="name" FooterText="Name" HeaderText="Фамилия"></asp:BoundColumn> <asp:BoundColumn DataField="nationality" FooterText="Country" HeaderText="Страна"></asp:BoundColumn> <asp:BoundColumn DataField="winningdate" FooterText="Year won" HeaderText="Год"></asp:BoundColumn> <asp:BoundColumn DataField="work" FooterText="Work" HeaderText="Произведение"></asp:BoundColumn> </Columns> <FooterStyle Font-Bold="True" Font-Italic="False" Font- Overline="False" Font-Strikeout="False" Font-Underline="False" /> <HeaderStyle Font-Bold="True" Font-Italic="False" Font- Overline="False" Font-Strikeout="False" Font-Underline="False" /> </asp:DataGrid>
Добавим в эту форму возможность сортировки по столбцам. DataGrid поддерживает свойство AllowSorting.
Но это только потенциальная возможность сортировки, так как сам элемент сортировать не может, это должен обеспечить программист. При AllowSorting = True в заголовке выводятся гиперссылки, при нажатии на которые вызывается событие SortCommand. Так как нет автогенерации, в описание BoundColumn нужно вставить SortExpression:
<asp:BoundColumn DataField="name" FooterText="Name" HeaderText="Фамилия" SortExpression="name"> </asp:BoundColumn>
Метод-обработчик события SortCommand принимает параметр типа DataGridSortCommandEventArgs, в свойстве SortExpression которого содержится строка — выражение сортировки SortExpression. Необходимо использовать это выражение для сортировки данных, полученных из источника данных
protected void DataGrid2_SortCommand(object source, DataGridSortCommandEventArgs e) { ViewState["sort"] = e.SortExpression; bindData(); }
где bindData() вынесен в отдельную функцию и вызывается также из Page_Load:
private void bindData() { String xmlFilename = Server.MapPath("") + "\\nobel.xml"; DataSet newDataSet = new DataSet(); newDataSet.ReadXml(xmlFilename); if (ViewState["sort"] != null) newDataSet.Tables[3].DefaultView.Sort = (string)ViewState["sort"]; DataTable newDataTable = newDataSet.Tables[3]; DataGrid2.DataSource = newDataTable; DataGrid2.DataBind(); }
void Page_Load() { if (!IsPostBack) bindData(); }
Romain Rollan | France | 1915 | "Jean-Cristophe" |
George Bernard Shaw | Great Britain | 1925 | |
Boris Pasternak | Russia | 1958 | "Doctor Zhivago" |
Gabriel Garsia Marquez | Columbia | 1982 | "100 years of solitude" |
DataGrid поддерживает возможность разбиения на страницы, но для этого тоже приходится писать код обработчиков событий. В WebMatrix имеются шаблоны таких страниц. С появлением GridView такую технику можно считать устаревшей, так как GridView позволяет делать все это с помощью одного только декларативного связывания.
Покажем возможность удаления, обновления и редактирования данных в DataGrid с помощью SqlDataSource.
Создайте на сервере SQL в базе DemoBase таблицу Users с тремя полями:
UID | int | |
Name | varchar(50) | |
Comments | varchar(250) | + |
IsRegistered | bit | + |
Будем работать с таблицей с помощью трех процедур.
CREATE PROCEDURE dbo.SelectUsers ( @Col INT = 0 ) AS IF @Col = 0 SELECT * FROM Users ELSE IF @Col = 1 SELECT * FROM Users ORDER BY NAME RETURN
Процедура EditUser будет использоваться для вставки записей, если @UID, и для обновления в противном случае:
CREATE PROCEDURE dbo.EditUser ( @UID int = 0, @Name varchar(50), @Comments varchar(250), @Registered bit ) AS IF @UID = 0 INSERT INTO Users(Name, Comments, IsRegistered) VALUES(@Name, @Comments, @Registered) ELSE UPDATE Users SET Name = @Name, Comments = @Comments, IsRegistered = @Registered WHERE UID = @UID RETURN
Процедура для удаления записей.
CREATE dbo.DeleteUser ( @UID int ) AS DELETE FROM Users WHERE UID = @UID RETURN
На форме будет находиться, кроме DataGrid, два элемента редактирования — NameTextBox, а также CommentTextBox и кнопка Add. Для наших тайных целей добавим элемент управления типа HiddenField. Эти цели — хранить id текущего элемента и передавать его SqlDataSource.
Добавим на форму следующий источник данных:
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:DemoBaseConnectionString %>" SelectCommand="dbo.SelectUsers" SelectCommandType="StoredProcedure" DeleteCommand="dbo.DeleteUser" DeleteCommandType="StoredProcedure" UpdateCommand="dbo.EditUser" UpdateCommandType="StoredProcedure" InsertCommand="dbo.EditUser" InsertCommandType="StoredProcedure" OldValuesParameterFormatString=""> <InsertParameters> <asp:Parameter Direction="ReturnValue" Name="RETURN_VALUE" Type="Int32" /> <asp:Parameter Name="UID" Type="Int32" DefaultValue="0" /> <asp:ControlParameter Name="Name" Type="String" ControlID="NameTextBox"/> <asp:ControlParameter Name="Comments" Type="String" ControlID="CommentTextBox" PropertyName="Text"/> <asp:ControlParameter Name="Registered" Type="Boolean" ControlID="Registered" PropertyName="Checked"/> </InsertParameters> <UpdateParameters> <asp:Parameter Direction="ReturnValue" Name="RETURN_VALUE" Type="Int32" /> <asp:ControlParameter Name="UID" Type="Int32" ControlID="HiddenField1" /> <asp:ControlParameter Name="Name" Type="String" ControlID="NameTextBox"/> <asp:ControlParameter Name="Comments" Type="String" ControlID="CommentTextBox"/> <asp:ControlParameter Name="Registered" Type="Boolean" ControlID="Registered" PropertyName="Checked"/> </UpdateParameters> <DeleteParameters> <asp:ControlParameter ControlID="HiddenField1" PropertyName="Value" Name="UID" Type="Int32" /> </DeleteParameters> </asp:SqlDataSource>
Обратите внимание на то, что в InsertParameters значение параметра UID по умолчанию 0 и он не связан с элементом управления. В остальных случаях он связан с HiddenField1. Значение в это поле будет передаваться в обработчиках.
На этот раз DataGrid будет содержать шаблонизированные столбцы TemplateColumn. Этот тип столбца DataGrid позволяет полностью управлять форматом отображения и редактирования данных — можно выводить данные в несколько строк или использовать для редактирования данных любые элементы управления. Например, для отображения булевской информации используем элементы CheckBox:
<asp:DataGrid id="UsersDataGrid" runat="server" AutoGenerateColumns="False" DataKeyField="UID" OnDeleteCommand="UsersDataGrid_DeleteCommand" OnUpdateCommand="UsersDataGrid_UpdateCommand" OnEditCommand="UsersDataGrid_EditCommand" DataSourceID="SqlDataSource1" AllowSorting="True" Caption="Users" CaptionAlign="Top"> <Columns> <asp:TemplateColumn HeaderText= "Имя"> <ItemTemplate> <asp:Label id="Label1" runat="server" Text='<%#DataBinder.Eval(Container, "DataItem.Name")%>'> </asp:Label> </ItemTemplate> <EditItemTemplate> <asp:TextBox id=NameTextBox runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Name") %>'> </asp:TextBox> </EditItemTemplate> </asp:TemplateColumn> <asp:TemplateColumn HeaderText="Комментарии"> <ItemTemplate> <asp:Label id="Label2" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Comments")%>'></asp:Label> </ItemTemplate> <EditItemTemplate> <asp:TextBox id= CommentTextBox runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Comments")%>'> </asp:TextBox> </EditItemTemplate> </asp:TemplateColumn> <asp:TemplateColumn HeaderText= "Зарегистрирован"> <ItemTemplate> <asp:CheckBox runat="server" ID="Registered" Checked='<%# (bool) DataBinder.Eval(Container, "DataItem.isRegistered") %>' Enabled="False"></asp:CheckBox > </ItemTemplate> <EditItemTemplate> <asp:CheckBox runat="server" ID="Registered" Checked='<%# (bool) DataBinder.Eval(Container, "DataItem.isRegistered") %>'></asp:CheckBox> </EditItemTemplate> </asp:TemplateColumn> <asp:TemplateColumn> <ItemTemplate> <asp:LinkButton id="LinkButton1" runat="server" CommandName="edit">редактировать</asp:LinkButton> :: <asp:LinkButton id="LinkButton2" runat="server" CommandName="delete">удалить</asp:LinkButton> </ItemTemplate> <EditItemTemplate> <asp:LinkButton id="LinkButton3" runat="server" CommandName="update">принять</asp:LinkButton> :: <asp:LinkButton id="LinkButton4" runat="server" CommandName="cancel">отменить</asp:LinkButton> </EditItemTemplate> </asp:TemplateColumn> </Columns> </asp:DataGrid>
И остальные элементы:
<asp:TextBox ID="NameTextBox" runat="server" ></asp:TextBox> <asp:TextBox ID="CommentTextBox" runat="server"></asp:TextBox> <asp:CheckBox ID="Registered" runat="server" /><br /><br /> <asp:Button ID="Button1" runat="server" Text="Add" OnClick="Add_Click" /> <asp:HiddenField ID="HiddenField1" runat="server" />
DataGrid уже будет выводить данные, имеет гиперссылки для правки и удаления, но при нажатии ничего не происходит. Остается написать нужный код. SqlDataSource уже знает параметры команды Delete, это единственный параметр, и связан он был с HiddenField1. Нужно записать значение ключа id в это поле:
protected void UsersDataGrid_DeleteCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) { HiddenField1.Value = UsersDataGrid.DataKeys[e.Item.ItemIndex].ToString(); // Удаление SqlDataSource1.Delete(); // Обновление данных после удаления UsersDataGrid.DataBind(); }
Добавление записи происходит еще проще, так как параметры процедуры Insert находятся в тех текстовых полях, которые заполняются для вставки:
protected void Add_Click(object sender, EventArgs e) { SqlDataSource1.Insert(); UsersDataGrid.DataBind(); }
В момент начала редактирования текстовые поля прячутся, так как значения из полей редактирования внутри таблицы будут копироваться туда, и нужно скрыть это от пользователя.
Кроме того, эти элементы управления во время редактирования не нужны:
protected void UsersDataGrid_EditCommand(object source, DataGridCommandEventArgs e) { UsersDataGrid.EditItemIndex = e.Item.ItemIndex; NameTextBox.Visible = false; CommentTextBox.Visible = false; Registered.Visible = false; UsersDataGrid.DataBind(); }
Редактирование — сложнее. В шаблоне столбцов элемента DataGrid указаны текстовые поля, которые появляются после нажатия на ссылку "Редактировать". Значения параметров надо брать оттуда, но их нельзя указать в декларации SqlDataSource, потому что в момент генерации страницы их там просто нет. Например, для того чтобы установить в режим редактирования строку DataGrid, необходимо присвоить свойству EditItemIndex элемента управления DataGrid значение индекса текущей строки.
В событии UpdateCommand в аргументе e находим текущую строку. Обновляемые данные находятся в TextBox-ах в ячейках 0 и 1. Чтобы выйти из режима редактирования, свойству EditItemIndex нужно присвоить значение - 1:
protected void UsersDataGrid_UpdateCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) { TableCell cell=e.Item.Cells[0]; TextBox cnt = (TextBox)cell.FindControl("NameTextBox"); NameTextBox.Text = cnt.Text;
cell = e.Item.Cells[1]; cnt = (TextBox)cell.FindControl("CommentTextBox");
CommentTextBox.Text = cnt.Text; HiddenField1.Value = UsersDataGrid.DataKeys[e.Item.ItemIndex].ToString(); cell = e.Item.Cells[2]; CheckBox check = (CheckBox)cell.FindControl("Registered"); Registered.Checked = check.Checked; SqlDataSource1.Update();
UsersDataGrid.EditItemIndex = -1;
UsersDataGrid.DataBind(); NameTextBox.Text = ""; CommentTextBox.Text = ""; NameTextBox.Visible = true; CommentTextBox.Visible = true; Registered.Visible = true; }
После обновления вновь читаем данные и делаем видимыми поля редактирования.
Отказ от редактирования без сохранения изменений обрабатывается в событии CancelCommand:
protected void UsersDataGrid_CancelCommand(object source, DataGridCommandEventArgs e) { UsersDataGrid.EditItemIndex = -1; NameTextBox.Visible = true; CommentTextBox.Visible = true; UsersDataGrid.DataBind(); }
DataList
DataList имеет те же черты, что и Repeater, то есть выводит данные согласно шаблонам. Однако это более богатый элемент управления. Во-первых, он поддерживает выбор, редактирование, удаление и вставку. Поэтому список шаблонов пополнился SelectedItemTemplate и EditItemTemplate. Кроме того, у него есть верхний и нижний колонтитулы со стилями HeaderStyle и FooterStyle.
Во-вторых, можно изменить способы отображения. По умолчанию DataList выводит данные поколонно в таблице. Свойство RepeatLayout, установленное как Flow, убирает табличные теги из выходного потока. RepeatDirection меняет направление вывода с вертикального на горизонтальное. RepeatColumns задает количество столбцов таблицы, по умолчанию равное 1.
DataList — наследник абстрактного класса BaseDataList, который наследует WebControl. Поэтому у него, в отличие от Repeater, имеются визуальные свойства. При отображении он представляет собой таблицу, поэтому присутствуют свойства CellPadding и CellSpacing.
У DataList есть шаблон по умолчанию, Visual Studio 2005 и VWD создают его в виде вертикально расположенных меток для каждого поля, а слева от них помещают текст с названием поля. Чтобы войти в режим редактирования шаблона, нужно воспользоваться возможностью SmartTag — Edit Templates. После того, как редактирование окончено, не забудьте выйти из режима — End Template Editing.
Можно спроектировать этот элемент так, чтобы в обычном состоянии отображалась краткая информация, а в выбранном состоянии — более подробная.
Посмотрим пример из Quickstarts:
<asp:DataList id="DataList1" runat="server" BorderColor="black" BorderWidth="1" GridLines="Both" CellPadding="3" Font-Names="Verdana" Font-Size="8pt" Width="150px" HeaderStyle-BackColor="#aaaadd" AlternatingItemStyle-BackColor="Gainsboro" SelectedItemStyle-BackColor="yellow" OnItemCommand="DataList_ItemCommand" > <HeaderTemplate> Items </HeaderTemplate> <ItemTemplate> <asp:LinkButton id="button1" runat="server" Text="Show details" CommandName="select" /> <%# DataBinder.Eval(Container.DataItem, "StringValue") %> </ItemTemplate> <SelectedItemTemplate> Item: <%# DataBinder.Eval(Container.DataItem, "StringValue") %> <br> Order Date: <%# DataBinder.Eval(Container.DataItem, "DateTimeValue", "{0:d}") %> <br> Quantity: <%# DataBinder.Eval(Container.DataItem, "IntegerValue", "{0:N1}") %> <br> </SelectedItemTemplate> </asp:DataList>
А обработчик выбора записи такой:
void DataList_ItemCommand(object Sender, DataListCommandEventArgs e) { string cmd = ((LinkButton)e.CommandSource).CommandName; if (cmd == "select") DataList1.SelectedIndex = e.Item.ItemIndex; BindList(); }
Чтобы реализовать редактирование, тоже нужно обрабатывать событие. Поэтому в ASP .NET 2.0 DataList лучше применять для показа данных без редактирования, а если редактирование все же требуется — использовать элемент управления FormView.
Свойство DataKeyField имеется и у DataGrid, и у DataList. С помощью него происходит связывание с ключевым полем таблицы данных.
Элементы-потребители данных
Потребители данных отображают данные, полученные из классов-источников данных. Они предоставляют много полезных функций. Например, элемент управления GridView может не только показывать данные, но и сортировать, выбирать, редактировать их. Если этой функциональности недостаточно, ее можно расширить, написав собственные обработчики событий.
Элементы, которые могут быть связаны с элементами-источниками данных, многообразны. Во-первых, это уже хорошо знакомые DropDownList, ListBox, CheckBoxList, RadioButtonList, BulletedList. Однако у всех них необходимо в качестве источника данных указывать не DataSource, а DataSourceID. Все эти элементы отображать могут только одно поле, указанное в DataTextField, с возможностью задания второго в качестве индексного в свойстве DataValueField:
<asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" SelectCommand="SELECT [CategoryName], [CategoryID] FROM [Categories]"> </asp:SqlDataSource> <asp:CheckBoxList ID="CheckBoxList1" runat="server" AppendDataBoundItems="True" DataSourceID="SqlDataSource3" DataTextField="CategoryName" DataValueField="CategoryID"> </asp:CheckBoxList> <asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="SqlDataSource3" DataTextField="CategoryName"> </asp:DropDownList> <asp:BulletedList ID="BulletedList1" runat="server" BulletStyle="UpperRoman" DataSourceID="SqlDataSource3" DataTextField="CategoryName" DataValueField="CategoryName"> </asp:BulletedList>
AppendDataBoundItems — это новое свойство. Оно позволяет комбинировать данные из элемента-источника с данными, статически объявленными на странице.
Очень интересны элементы управления Repeater и DataList. Они позволяют управлять отображением данных с помощью шаблонов.
Repeater
Repeater в переводе означает "тот, кто повторяет". Среди его значений "студент-второгодник" и "вор-рецидивист". Ведь они тоже повторяют то, что делали. Но это "плохие" значения, а "хорошее" — элемент-повторитель заданного шаблона для всех полей источника данных.
Шаблон — это множество тегов HTML и серверных элементов управления, которые задают образец для отображения составной части сложного элемента управления. DataGrid может использовать шаблоны или нет, но Repeater без них существовать не может — сам по себе он не имеет визуального представления. Таким образом, программист сам определяет его внешний вид. Кроме DataSourceID и DataMember, собственных свойств у него нет. Поэтому у программиста есть полный контроль над тем, как выводится Repeater.
Как минимум, должен быть описан шаблон ItemTemplate. HeaderTemplate отображается один раз в начале отрисовки репитера, FooterTemplate в конце, SeparatorTemplate между отображением каждого пункта, AlternatingItemTemplate — для четных пунктов. Все серверные элементы управления в шаблон помещаются целиком, поэтому, чтобы получить таблицу, используют простые теги HTML. Например, открывающий тег <table> помещают в HeaderTemplate, а закрывающий — в FooterTemplate.
В этом примере составляются характеристики сотрудников:
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1" > <HeaderTemplate><asp:Label runat="server" ID="Header1" BackColor="DarkOrange" ForeColor="ActiveCaptionText" Font-Bold="true" Font-Underline="true" Font-Size="Large"> We present our valued Employees!</asp:Label> <table> </HeaderTemplate> <ItemTemplate> <tr> <td> <asp:Panel ID="Panel1" runat="server" BackColor="LightPink"> <asp:Image ID="Photo" runat="server" ImageUrl=<%# Eval("PhotoPath")%>/> <%# Eval( "TitleOfCourtesy") %> <%# DataBinder.Eval(Container.DataItem, "FirstName") %> <%# DataBinder.Eval(Container.DataItem, "LastName") %> was born in <%# Eval("BirthDate", "{0:dd.MM.yyyy}") %>.<p> <%# Eval("TitleOfCourtesy").ToString() == "Mr." || Eval("TitleOfCourtesy").ToString() == "Dr." ? "He" : "She"%> lives in beautiful city <%# ((System.Data.DataRowView)Container.DataItem)["City"]%>, <%# Eval( "Region") %> in <%# DataBinder.Eval(Container.DataItem, "Country") %>.</p> <p>We appreciate <%# Eval("TitleOfCourtesy").ToString() == "Mr." || Eval("TitleOfCourtesy").ToString() == "Dr." ? "his" : "her"%> work as <%# Eval("Title") %>.</p> <p><%# DataBinder.Eval(Container.DataItem, "Notes") %></p> </asp:Panel> </td> </tr> </ItemTemplate> <FooterTemplate> </table></FooterTemplate> </asp:Repeater> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString1 %>" SelectCommand="SELECT * FROM [Employees]" ></asp:SqlDataSource>
В браузере это выглядит так:
Рис. 8.1.
Обратите внимание на условные выражения:
<%# Eval("TitleOfCourtesy").ToString() == "Mr." || Eval("TitleOfCourtesy").ToString() == "Dr." ? "He" : "She"%>
Благодаря этому в предложении о мужчине употребляются местоимения мужского, а в предложении о женщине — женского рода. В таблице нет поля для пола, но его можно вычислить по полю TitleOfCourtesy, который у мужчин бывает или "Mr. ", или "Dr.".
Если нужно изменить внешний вид всего шаблона, проще всего поместить все его содержимое или весь элемент в Panel и определить в нем стили.
С помощью элемента управления Repeater можно создавать этикетки для конвертов, пригласительные билеты и так далее.
Синтаксис динамического связывания
Можно получать данные, связанные с элементом управления, в его декларации на странице. Это делается с помощью разновидности блока отображения. В ранних версиях ASP .NET с помощью такого механизма можно было только читать, но теперь возможно и двустороннее связывание. Особенно это важно в элементах управления, использующих шаблоны. Хотя DataGrid и GridView автоматически отображают данные, но и в них для создания нужных эффектов используются столбцы-шаблоны.
Для привязки к данным используются разделители <%# %>. В жизненном цикле каждого элемента управления наступает событие DataBinding, во время которого и вычисляются все выражения, заключенные в этот тег.
Если в функции Page_Load мы писали
ContinentDropDownList.DataSource = ContinentArrayList;
то на странице это можно сделать с помощью
<asp: DropDownList id=" ContinentDropDownList " datasource='<%# ContinentArrayList %>' runat="server">
В ASP .NET 1.x данные читались в объекты DataSet или DataReader, после чего вызывался DataBind. В ASP .NET 2.0, если же установлено свойство DataSourceID, то событие DataBinding вызывается автоматически.
В сгенерированном ASP .NET классе страницы в обработчике этого события для ContinentDropDownList будет выполняться код
datasource = ContinentArrayList;
При этом в локальном кэше создается копия прочтенных данных. Для элементов Repeater, DataList, DataGrid синтаксис привязки данных разбирается в событии ItemDataBound, для GridView — в RowDataBound.
Эти события вызываются столько раз, сколько записей в источнике данных. Каждый раз, когда ASP .NET встречает эти разделители в шаблоне, внутри элемента Item (RepeaterItem, DataListItem, DataGridItem) создается элемент типа DataBoundLiteralControl, внутри которого записывается выражение внутри разделителей. В обработчике события DataBinding этого элемента определена переменная Container, которая указывает на этот самый Item, то есть секцию элемента. Классы Item хранят данные в свойстве DataItem. Поэтому в шаблонах доступ к данным происходит с помощью синтаксиса Container.DataItem.
Возможный синтаксис для доступа к полю:
<%# ((System.Data.DataRowView)Container.DataItem)["City"]%>
в случае, когда данные получены из DataReader,
<%# ((System.Data.IDataRecord)Container.DataItem)["City"]%>
в случае, когда данные получены из DataSet,
или
<%# DataBinder.Eval(Container.DataItem, "City") %>
Этот способ заменяет оба предыдущие, так как DataBinder с помощью статической функции Eval сам определяет тип источника и соответственно его обрабатывает. Метод Eval перегружен, его второй вариант принимает аргумент для форматирования данных:
DataBinder.Eval(Container.DataItem, "Age","{0:2d}")
В ASP .NET 2.0 синтаксис можно упростить и написать
<%# Eval("City")%>
В новых элементах управления GridView, DetailsView, FormView, где поддерживается двунаправленный вывод данных, можно вызывать метод Bind. Его используют в шаблонах редактируемых строк:
<asp:TextBox ID="EditFirstNameTextBox" Runat="Server" Text='<%# Bind("FirstName") %>' />
Repeater использует только шаблоны, DataList
Мы рассмотрели 3 элемента управления, существовавшие с ASP .NET 1.0. Repeater использует только шаблоны, DataList создает таблицу и пользуется шаблоном для отображения ее строк, а DataGrid может обходиться и без шаблонов. Примерно такая же система существует и у новых элементов управления ASP .NET 2.0.
Объектная модель упрощает связывание с данными, но ее реальная мощь проявляется при использовании новых элементов управления GridView, DetailsView, FormView, которые мы рассмотрим в следующей лекции.