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(); }