Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » .NET - ASP.NET »

Обсудить на форуме Обсудить на форуме

Часто задаваемые вопросы о серверном Web-элементе управления DataGrid
Ответы на часто задаваемые вопросы об использовании серверного Web-элемента управления DataGrid.

Аннотация

Ответы на часто задаваемые вопросы об использовании серверного Web-элемента управления DataGrid.

Относится к:

  • Microsoft® Visual Studio® .NET;
  • ASP.NET;
  • Web Forms;
  • серверному Web-элементу управления DataGrid.

Введение

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

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

Здесь предполагается, что вы уже знакомы с этим элементом управления, умеете добавлять его на форму и настраивать для отображения данных. Кроме того, вы должны уметь вставлять строки в сетку в режиме редактирования и выполнять другие элементарные задачи (дополнительную информацию можно найти по ссылке DataGrid Web Server Control). Наконец, будет полезным умение работать с шаблонами - добавлять к сетке столбцы-шаблоны и размещать элементы управления внутри шаблона.

DataGrid в Windows Forms и в Web Forms

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

Из-за перечисленных отличий аналогичные элементы управления из Web Forms и Windows Forms (включая DataGrid) также отличаются. Как правило, в DataGrid из Web Forms меньше встроенной функциональности. Вот ряд отличий элемента управления DataGrid из Web Forms:

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

С другой стороны:

  • DataGrid из Web Forms можно связать с любым объектом, поддерживающим интерфейс IEnumerable;
  • DataGrid из Web Forms поддерживает постраничный просмотр (paging);
  • настроить внешний вид и разметку DataGrid из Web Forms проще, чем для аналогичного элемента из Windows Forms (подробнее об этом - ниже).

Управление шириной и высотой столбцов, а также выравниванием в ячейках

По умолчанию DataGrid автоматически регулирует размер строк и столбцов в соответствии с общими размерами сетки. В пределах общей ширины сетки DataGrid настраивает ширину столбцов по ширине текста в заголовке столбца. По умолчанию все данные выравниваются по левому краю.

Для управления параметрами столбцов отключите автоматическую генерацию столбцов (auto column generation), задав для свойства AutoGenerateColumns значение false. И вообще оставлять для этого свойства значение true следует лишь для быстрой проверки общей структуры сетки или в демонстрационных целях, но в рабочих приложениях столбцы нужно добавлять явным образом. Отдельные столбцы могут быть связанными с другими столбцами или создаваться на основе шаблона.

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

<asp:BoundColumn DataField="title" SortExpression="title" 
      HeaderText="Title">
   <ItemStyle Width="100px"></ItemStyle>
</asp:BoundColumn>

То же самое можно сделать, задав свойство ItemStyle прямо в элементе, как в следующем примере:

<asp:BoundColumn ItemStyle-Width="100px" DataField="title" 
   SortExpression="title" HeaderText="Title">
</asp:BoundColumn>

Этот же элемент стиля позволяет указать тип выравнивания, задавая "Right", "Left" и другие значения, определенные в перечислении HorizontalAlign. (В Visual Studio можно задавать тип выравнивания в отдельных столбцах, используя вкладку Format в окне свойств сетки.) Вот пример:

<asp:BoundColumn DataField="title" SortExpression="title" 
      HeaderText="Title">
   <ItemStyle Width="100px" HorizontalAlign="Right"></ItemStyle>
</asp:BoundColumn>

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

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

' Visual Basic
Private Sub DataGrid1_ItemCreated(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) _
    Handles DataGrid1.ItemCreated
        e.Item.Cells(0).Width = New Unit(100)
        e.Item.Cells(1).Width = New Unit(50)
    End Sub

// C#
private void DataGrid1_ItemCreated(object sender, 
System.Web.UI.WebControls.DataGridItemEventArgs e)
{
   e.Item.Cells[0].Width = new Unit(100);
   e.Item.Cells[1].Width = new Unit(50);
}

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

Настройка вида столбцов в режимах отображения и редактирования

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

Если вы хотите изменить содержимое некоего столбца, сделайте его шаблоном. Столбцы-шаблоны работают подобно шаблонам элементов (item templates) в элементах управления DataList и Repeater с тем исключением, что в этом случае определяется вид столбца, а не строки.

Определяя столбец-шаблон, можно использовать следующие типы шаблонов:

  • ItemTemplate - позволяет настроить обычное отображение данных;
  • EditItemTemplate - позволяет задать, что будет показано в столбце при переводе строки в режим редактирования. Именно это дает возможность использовать при редактировании ячейки какой-либо элемент управления, отличного от текстового поля по умолчанию;
  • HeaderTemplate и FooterTemplate - позволяют настраивать верхний и нижний колонтитулы соответственно (нижний колонтитул отображается, только если свойство ShowFooter сетки установлено в true).

Следующий пример демонстрирует синтаксис HTML для столбца-шаблона, отображающего данные типа Boolean. Как ItemTemplate, так и EditItemTemplate выводят значения в виде флажка. В ItemTemplate флажок отключен (показывается серым), чтобы пользователи не подумали, будто они могут самостоятельно сбросить (или установить) его. В EditItemTemplate флажок включен.

<Columns>
<asp:TemplateColumn HeaderText="Discontinued">
<ItemTemplate>
   <asp:Checkbox runat="server" enabled= false name ="Checkbox2" 
     ID="Checkbox2" 
     Checked = '<%# DataBinder.Eval(Container, 
"DataItem.Discontinued") %>' >
   </asp:Checkbox>
</ItemTemplate>
<EditItemTemplate>
   <asp:Checkbox 
      runat="server" name ="Checkbox2" ID="Checkbox2" 
      Checked = '<%# DataBinder.Eval(Container, 
"DataItem.Discontinued") %>' >
   </asp:Checkbox>
</EditItemTemplate>
</asp:TemplateColumn>
</Columns>

Примечание. Если вы используете в EditItemTemplate элемент управления CheckBox, учтите, что в период выполнения в ячейке, помимо собственно флажка, будет несколько элементов управления LiteralControl (для поддержки интервалов). Если вы знаете идентификатор нужного вам элемента управления, используйте метод FindControl, чтобы создать ссылку на него, а не индексы в наборах Cells и Controls.

' Visual Basic
Dim cb As CheckBox
cb = CType(e.Item.FindControl("CheckBox2"), CheckBox)

// C#
CheckBox cb;
cb = (CheckBox) e.Item.FindControl("CheckBox2");

В Visual Studio можно использовать окно свойств сетки, чтобы создать столбец-шаблон, и задать вид шаблона при помощи редактора шаблона. На вкладке Columns в окне свойств сетки выберите нужный столбец и внизу щелкните Convert this column into a Template Column. Закройте окно свойств, щелкните сетку правой кнопкой мыши и выберите Edit Template. Далее вы можете перетаскивать элементы управления из Toolbox на шаблон и добавлять статический текст.

Форматирование дат, денежных сумм и других данных

Данные, которые содержит элемент управления DataGrid, в конечном счете отображаются в виде HTML-таблицы на странице Web Forms. Поэтому вы можете управлять тем, как выводятся данные, указав один из видов форматирования текста в столбцах. Однако этого нельзя сделать для столбцов, чье свойство AutoGenerateColumns установлено в true. Такая операция возможна лишь для связанных столбцов или столбцов-шаблонов.

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

Примечание. Visual Studio позволяет указывать форматирующее выражение на вкладке Columns окна свойств элемента управления.

В следующей таблице приведено несколько образцов форматирующих строк. Дополнительную информацию см. в документации Visual Studio (разделы Formatting Types и BoundColumn.DataFormatString Property).

 

Форматирующее выражение Применимо к данным типа Описание
Price: {0:C}
Примечание.{0} - это нуль, а не буква О.
числовой/десятичный Выводит литерал "Price:", за которым следует число в денежном формате. Формат зависит от культуры, заданной соответствующим атрибутом в директиве Page или файле Web.config.
{0:D4} целый (нельзя использовать с данными типа decimal) Выводятся целочисленные данные в поле длиной в четыре символа. Число, содержащие меньше четырех разрядов, дополняются нулями.
{0:N2}% числовой Выводятся числа с точностью до второго знака после запятой, после которых следует литерал "%".
{0:000.0} числовой/десятичный Дробная часть числа округляется до одного десятичного знака; числа, содержащие менее трех разрядов, дополняются нулями.
{0:D} дата/дата и время Длинный формат даты (например, "Thursday, August 06, 1996"). Формат даты зависит от культуры заданной на странице или в файле Web.config.
{0:d} дата/дата и время Краткий формат даты (например, "12/31/99")
{0:yy-MM-dd} дата/дата и время Дата в числовом формате "год-месяц-день" (например, 96-08-06).

Динамическое отображение и скрытие строк

Один из способов динамического вывода столбцов - создавать их на этапе разработки, а затем по мере необходимости показывать или скрывать. Для этого изменяйте значение свойства Visible столбца. Следующий пример показывает, как показывать/скрывать второй столбец (с индексом 1) сетки:

' Visual Basic
DataGrid1.Columns(1).Visible = Not (DataGrid1.Columns(1).Visible)

// C#
DataGrid1.Columns[1].Visible = !(DataGrid1.Columns[1].Visible);

Динамическое добавление столбцов

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

С этой целью нужно создать экземпляр одного из классов столбцов, поддерживаемых сеткой: BoundColumn, EditCommandColumn, ButtonColumn или HyperlinkColumn. (Вы можете добавлять к сетке столбцы-шаблоны, но это несколько сложнее. Детали см. в статье Creating Web Server Control Templates Programmatically.) Установите свойства столбца и добавьте его к набору Columns сетки.

Следующий пример показывает, как добавить к сетке два связанных столбца (bound columns).

' Visual Basic
Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click
   ' Задать свойства, определяющие привязку к данным для сетки
   DataGrid1.AutoGenerateColumns = False
   DataGrid1.DataSource = Me.dsBooks1
   DataGrid1.DataMember = "Books"
   DataGrid1.DataKeyField = "bookid"

   ' Добавить два столбца
   Dim dgc_id As New BoundColumn()
   dgc_id.DataField = "bookid"
   dgc_id.HeaderText = "ID"
   dgc_id.ItemStyle.Width = New Unit(80)
   DataGrid1.Columns.Add(dgc_id)

   Dim dgc_title As New BoundColumn()
   dgc_title.DataField = "title"
   dgc_title.HeaderText = "Title"
   DataGrid1.Columns.Add(dgc_title)

   Me.SqlDataAdapter1.Fill(Me.dsBooks1)
   DataGrid1.DataBind()
End Sub

// C#
private void Button1_Click(object sender, System.EventArgs e)
{
   DataGrid1.AutoGenerateColumns = false;
   DataGrid1.DataSource = this.dsBooks1;
   DataGrid1.DataMember = "Books";
   DataGrid1.DataKeyField = "bookid";

   // Добавить два столбца
   BoundColumn dgc_id = new BoundColumn();
   dgc_id.DataField = "bookid";
   dgc_id.HeaderText = "ID";
   dgc_id.ItemStyle.Width = new Unit(80);
   DataGrid1.Columns.Add(dgc_id);

   BoundColumn dgc_title= new BoundColumn();
   dgc_title.DataField = "title";
   dgc_title.HeaderText = "Title";
   DataGrid1.Columns.Add(dgc_title);

   this.sqlDataAdapter1.Fill(this.dsBooks1);
   DataGrid1.DataBind();
}

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

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

Следующий пример демонстрирует, как можно дополнить предыдущий пример для восстанавления столбцов при каждом повторном запуске страницы. Как и раньше, обработчик Button1_Click добавляет к сетке два столбца (в данном случае обработчик вызывает с этой целью отдельную процедуру AddColumns). Кроме того, страница содержит простое булево свойство DynamicColumnsAdded, указывающее, были ли добавлены столбцы к сетке; значение этого свойства сохраняется в состоянии отображения страницы. Метод LoadViewState сначала вызывает одноименный метод базового класса, который извлекает сведения о состоянии отображения и конфигурирует элементы управления на основе полученных данных. Если столбцы уже были добавлены к сетке (что можно узнать, проверив свойство DynamicColumnsAdded), метод добавляет из снова.

' Visual Basic
Private Property DynamicColumnAdded() As Boolean
   Get
      If ViewState("ColumnAdded") Is Nothing Then
         Return False
      Else
         Return True
      End If
   End Get
   Set(ByVal Value As Boolean)
      ViewState("ColumnAdded") = Value
   End Set
End Property

Protected Overrides Sub LoadViewState(ByVal savedState As Object)
   MyBase.LoadViewState(savedState)
   If Me.DynamicColumnAdded Then
      Me.AddColums()
   End If
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click
   ' Убедиться, что столбы добавляются только один раз
   If Me.DynamicColumnAdded Then
      Return
   Else
      Me.AddColums()
   End If
End Sub

Protected Sub AddColums()
   ' Добавить два столбца
   Dim dgc_id As New BoundColumn()
   dgc_id.DataField = "instock"
   dgc_id.HeaderText = "In Stock?"
   dgc_id.ItemStyle.Width = New Unit(80)
   DataGrid1.Columns.Add(dgc_id)

   Dim dgc_title As New BoundColumn()
   dgc_title.DataField = "title"
   dgc_title.HeaderText = "Title"
   DataGrid1.Columns.Add(dgc_title)
   Me.DataGrid1.DataBind()
   Me.DynamicColumnAdded = True
End Sub

// C#
private bool DynamicColumnAdded{
   get
   {
      object b = ViewState["DynamicColumnAdded"];
      return (b == null) ? false : true;
   }
   set
   {
      ViewState["DynamicColumnAdded"] = value;
   }
}

protected override void LoadViewState(object savedState)
{
   base.LoadViewState(savedState);
   if (DynamicColumnAdded) 
   {
      this.AddColumns();
   }
}

private void Button1_Click(object sender, System.EventArgs e)
{
   if(this.DynamicColumnAdded != true)
   {
      this.AddColumns();
   }
}


private void AddColumns()
{
   BoundColumn dgc_id = new BoundColumn();
   dgc_id.DataField = "bookid";
   dgc_id.HeaderText = "ID";
   dgc_id.ItemStyle.Width = new Unit(80);
   DataGrid1.Columns.Add(dgc_id);

   BoundColumn dgc_title= new BoundColumn();
   dgc_title.DataField = "title";
   dgc_title.HeaderText = "Title";
   DataGrid1.Columns.Add(dgc_title);

   this.sqlDataAdapter1.Fill(this.dsBooks1);
   DataGrid1.DataBind();
   this.DynamicColumnAdded = true;
}

Добавление новых записей к источнику данных через DataGrid

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

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

Следующий пример демонстрирует, как добавить новую запись, выполнить привязку сетки и перевести ее в режим редактирования. Здесь источником данных является объект DataSet (DsBooks1 или dsBooks1), содержащий таблицу Books.

' Visual Basic
Private Sub btnAddRow_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles btnAddRow.Click
   Dim dr As DataRow = Me.DsBooks1.Books.NewRow
   dr("title") = "(New)"
   dr("instock") = True
   Me.DsBooks1.Books.Rows.InsertAt(dr, 0)
   Session("DsBooks") = DsBooks1
   DataGrid1.EditItemIndex = 0
   DataGrid1.DataBind()
End Sub

// C#
private void btnAddRow_Click(object sender, System.EventArgs e)
{
   DataRow dr = this.dsBooks1.Books.NewRow();
   dr["title"] = "(New)";
   dr["instock"] = true;
   this.dsBooks1.Books.Rows.InsertAt(dr, 0);
   Session["DsBooks"] = dsBooks1;
   DataGrid1.EditItemIndex = 0;
    DataGrid1.DataBind();
}

Обратите внимание:

  • этот код выполняется, когда пользователь щелкает кнопку Add, расположенную на странице;
  • новая строка создается с применением метода NewRow. Она вставляется в таблицу набора данных методом InsertAt, который позволяет выполнить вставку в заранее определенном месте (в нашем случае - в начало таблицы, т. е. вставить строку как первую в наборе Rows). Альтернативный вариант - добавить строку в конец таблицы, учитывая общее число строк. Важно точно знать местоположение строки в таблице;
  • поскольку известно, что строка занимает первую позицию в таблице, можно занести в свойство EditItemIndex сетки нуль, чтобы перевести новую строку в режим редактирования (если созданная строка вставлена в другую позицию, укажите соответствующий индекс в свойстве EditItemIndex);
  • поскольку новая запись имеется пока только в DataSet (но еще не записана в базу данных), вы должны сохранять копию DataSet между обращениями к странице, чтобы не "затереть" новую запись при повторном заполнении DataSet информацией из базы данных. Вот код, сохраняющий DataSet в сеансовой переменной Session. При загрузке страницы нужно заново загрузить набор данных из Session. Вот как, например, мог бы выглядеть ваш обработчик Page_Load:
' Visual Basic
Private Sub Page_Load(ByVal sender As System.Object, _
       ByVal e As System.EventArgs) Handles MyBase.Load
   If Me.IsPostBack Then
      DsBooks1 = CType(Session("DsBooks"), dsBooks)
   Else
      Me.SqlDataAdapter1.Fill(Me.DsBooks1)
      Session("DsBooks") = DsBooks1
      DataGrid1.DataBind()
   End If
End Sub

// C#
private void Page_Load(object sender, System.EventArgs e)
{
   if(this.IsPostBack)
   {
      dsBooks1 = (dsBooks) Session["DsBooks"];
   }
   else
   {
      this.sqlDataAdapter1.Fill(this.dsBooks1);
      Session["DsBooks"] = dsBooks1;
      this.DataGrid1.DataBind();
   }
}

Дополнительные сведения о сохранении информации о состоянии см. в разделе Web Forms State Management документации по Visual Studio.

Новую запись можно модифицировать, как обычно (пример см. в разделе Walkthrough: Using a DataGrid Web Control to Read and Write Data в документации по Visual Studio). Вот пример соответствующего обработчика:

' Visual Basic
Private Sub DataGrid1_UpdateCommand(ByVal source As Object, _
       ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _
       Handles DataGrid1.UpdateCommand
   Dim dr As Dataset.BooksRow
   ' Получить ссылку на нулевую строку (куда вставлена новая строка)
   dr = Me.DsBooks1.Books(0)
   Dim tb As TextBox = CType(e.Item.Cells(2).Controls(0), TextBox)
   dr.title = tb.Text
   Dim cb As CheckBox = CType(e.Item.Cells(3).Controls(1), CheckBox)
   dr.instock = cb.Checked
   Me.SqlDataAdapter1.Update(Me.DsBooks1)
   DataGrid1.EditItemIndex = -1
   ' Обновить DataSet данными из базы данных
   DsBooks1.Clear()
   Me.SqlDataAdapter1.Fill(Me.DsBooks1)
   ' Снова сохранить обновленный DataSet в переменной состояния Session
   Session("DsBooks") = DsBooks1
   DataGrid1.DataBind()
End Sub

// C#
private void DataGrid1_UpdateCommand(object source, 
      System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
   dsBooks.BooksRow dr;
   // Получить ссылку на нулевую строку (куда вставлена новая строка)
   dr = this.dsBooks1.Books[0];
   TextBox tb1 = (TextBox) e.Item.Cells[2].Controls[0];
   dr.title = tb1.Text;
   CheckBox cb = (CheckBox) e.Item.Cells[3].Controls[1];
   dr.instock = cb.Checked;
   this.sqlDataAdapter1.Update(this.dsBooks1);
   DataGrid1.EditItemIndex = -1;
   // Обновить DataSet данными из базы данных
   dsBooks1.Clear();
   this.sqlDataAdapter1.Fill(this.dsBooks1);
   // Снова сохранить обновленный DataSet в переменной состояния Session
   Session["DsBooks"] = dsBooks1;
   DataGrid1.DataBind();
}

Отображение раскрывающегося списка в режиме редактирования

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

Для отображения такого списка в сетке нужен столбец-шаблон. Как правило, ItemTemplate содержит элемент управления вроде связанного с данными Label, который показывает текущее значение поля в записи. К EditItemTemplate вы добавляете раскрывающийся список. В Visual Studio можно добавить столбец-шаблон в редакторе свойств сетки, после чего воспользоваться стандартной процедурой редактирования шаблона, чтобы удалить из EditItemTemplate расположенный там по умолчанию TextBox и перетащить на его место DropDownList. Альтернативный вариант - добавить столбец-шаблон в режиме редактирования HTML-текста.

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

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

Статические элементы

Для отображения статических элементов в раскрывающемся списке привязка к данным не нужна - достаточно определить элементы в наборе Items. В Visual Studio можно вызвать редактор набора Items через свойство Items в окне свойств, либо добавить элементы в режиме просмотра HTML-текста.

Следующий код иллюстрирует определение столбца-шаблона, показывающего жанр в режиме отображения и статический список жанров в режиме редактирования. В ItemTemplate находится Label, чье свойство Text связано с полем жанра (genre) в текущей записи. В исходном коде выделены объявления статических элементов в EditItemTemplate.

<asp:TemplateColumn HeaderText="genre">
   <ItemTemplate>
      <asp:Label id=Label4 runat="server" 
         Text='<%# DataBinder.Eval(Container, "DataItem.genre") %>'>
      </asp:Label>
   </ItemTemplate>
   <EditItemTemplate>
      <asp:DropDownList id="DropDownList2" runat="server" Width="172px">
         <asp:ListItem Value="fiction">fiction</asp:ListItem>
         <asp:ListItem Value="biography">biography</asp:ListItem>
         <asp:ListItem Value="reference">reference</asp:ListItem>
      </asp:DropDownList>
   </EditItemTemplate>
</asp:TemplateColumn>

Набор данных

Если информация, которую вы хотите показать в раскрывающемся списке, находится в наборе данных (dataset), используйте обычной привязку к данным. Элемент управления DropDownList связан с таблицей Genre, находящейся в наборе данных с именем DsBooks1. В исходном коде, показанном ниже, выделен фрагмент, где настраиваются параметры привязки к данным.

<asp:TemplateColumn HeaderText="genre (dataset)">
   <ItemTemplate>
      <asp:Label id=Label3 runat="server"
          Text='<%# DataBinder.Eval(Container, "DataItem.genre") %>'>
      </asp:Label>
   </ItemTemplate>
   <EditItemTemplate>
      <asp:DropDownList id=DropDownList4 runat="server" 
         DataSource="<%# DsBooks1 %>" DataMember="Genre" 
         DataTextField="genre" DataValueField="genre" Width="160px">
      </asp:DropDownList>
   </EditItemTemplate>
</asp:TemplateColumn>

Объект DataReader

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

Относительно простой путь реализации этого подхода - задействовать преимущества выражений, связывающих с данными в Web Forms. Хотя чаще всего при использовании такого выражения вызывают метод DataBinder.Eval, на самом деле можно вызывать любой открытый член, доступный для страницы. Следующий пример демонстрирует, как создать функцию, которая создает, заполняет и возвращает объект DataTable, с которым можно связать раскрывающийся список.

В этом сценарии потребуется выполнить команду, извлекающую необходимые записи. Например, можно определить команду для выборки данных, у которой значением свойства CommandText будет "Select * from Genres". Чтобы упростить пример, предполагается, что объекты соединения и команды уже существует на странице.

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

Следующий пример показывает, как это делается. Здесь возвращаемая таблица состоит лишь из одного столбца ("genre"). Для заполнения раскрывающегося списка обычно требуется только один столбец (или два, если вы хотите вывести текст и значения в разных столбцах).

' Visual Basic
Public Function GetGenreTable() As DataTable
   Dim dtGenre As DataTable = New DataTable()
   If Application("GenreTable") Is Nothing Then
      Dim dr As DataRow
      Dim dc As New DataColumn("genre")
      dtGenre.Columns.Add(dc)
      Me.SqlConnection1.Open()
      Dim dreader As SqlClient.SqlDataReader = _
         Me.SqlCommand1.ExecuteReader()
      While dreader.Read()
         dr = dtGenre.NewRow()
         dr(0) = dreader(0)
         dtGenre.Rows.Add(dr)
      End While
      Me.SqlConnection1.Close()
   Else
      dtGenre = CType(Application("GenreTable"), DataTable)
   End If
   Return dtGenre
End Function

//C#
public DataTable GetGenreTable()
{
   DataTable dtGenre = new DataTable();
   if(Application["GenreTable"] == null)
   {
      DataRow dr;
      DataColumn dc = new DataColumn("genre");
               dtGenre.Columns.Add(dc);
      this.sqlConnection1.Open();
      System.Data.SqlClient.SqlDataReader dreader = 
          this.sqlCommand1.ExecuteReader();
      while(dreader.Read())
      {
          dr = dtGenre.NewRow();
         dr[0] = dreader[0];
         dtGenre.Rows.Add(dr);
      }
      this.sqlConnection1.Close();
   }
   else
   {
      dtGenre = (DataTable) Application["GenreTable"];
   }
   return dtGenre;
}

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

Следующий код демонстрирует объявление столбца шаблона. Вы убедитесь, что его синтаксис очень похож на синтаксис привязки к таблице из набора данных. Единственное существенное различие между ними в том, что при привязке к DataSource вызывается ваша функция. Небольшой недостаток этого подхода заключается в том, что при его использовании Visual Studio мало чем поможет вам во время разработки. Поскольку таблица, с которой будет связан список, определяется в коде, Visual Studio не предложит список возможных значения для свойств DataMember, DataTextField и DataValueField. Вам придется самостоятельно следить за тем, чтобы значения этих свойств соответствовали именам членов, создаваемых в коде.

<asp:TemplateColumn HeaderText="genre (database)">
   <ItemTemplate>
      <asp:Label id=Label1 runat="server" 
          Text='<%# DataBinder.Eval(Container, "DataItem.genre") %>'>
      </asp:Label>
   </ItemTemplate>
   <EditItemTemplate>
      <asp:DropDownList id=DropDownList1 runat="server"
         DataSource="<%# GetGenreTable() %>"
         DataMember="Genre" 
         DataTextField="genre" 
         DataValueField="genre" 
         Width="120px">
     </asp:DropDownList>
   </EditItemTemplate>
</asp:TemplateColumn>

Предварительное выделение элемента в раскрывающемся списке

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

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

Фокус в том, чтобы определить, какое значение нужно выделить в раскрывающемся списке. Как правило, это значение уже доступно либо как текущий (отображаемый) элемент, либо как свойство DataItem текущего элемента списка, возвращающее объект DataRowView с текущей записью. Получив нужное значение, вызовите метод FindByText или FindByValue элемента управления, чтобы найти в списке соответствующий элемент, после чего из свойства IndexOf этого элемента можно получить значение его индекса.

' Visual Basic
Private Sub DataGrid1_ItemDataBound(ByVal sender As Object, _
      ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) _
      Handles DataGrid1.ItemDataBound
   If e.Item.ItemType = ListItemType.EditItem Then
      Dim drv As DataRowView = CType(e.Item.DataItem, DataRowView)
      Dim currentgenre As String = CType(drv("genre"), String)
      Dim ddl As DropDownList
      ddl = CType(e.Item.FindControl("DropDownList1"), DropDownList)
      ddl.SelectedIndex = ddl.Items.IndexOf(ddl.Items.FindByText(currentgenre))
   End If
End Sub

// C#
private void DataGrid1_ItemDataBound(object sender, 
      System.Web.UI.WebControls.DataGridItemEventArgs e)
{
   if(e.Item.ItemType == ListItemType.EditItem){
      DataRowView drv = (DataRowView) e.Item.DataItem;
     String currentgenre = drv["genre"].ToString();
     DropDownList ddl = 
        (DropDownList) e.Item.FindControl("DropDownList1");
     ddl.SelectedIndex = 
         ddl.Items.IndexOf(ddl.Items.FindByText(currentgenre));
   }
}

Выделение нескольких элементов с помощью флажков (модель Hotmail)

В таких приложениях, как Microsoft Hotmail®, пользователи могут "выбирать" строки, помечая их флажки, и в дальнейшем выполнять над всеми выбранными строками некие операции (например, удаление или копирование).

Чтобы ввести подобную функциональность, добавьте к сетке столбец-шаблон и поместите в него флажок. При запуске страницы пользователь сможет пометить нужные ему элементы флажками.

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

' Visual Basic
Private Sub btnDelete_Click(ByVal sender As System.Object, _
       ByVal e As System.EventArgs) Handles btnDelete.Click
   ' Пройти по сетке и найти выбранные строки
   Dim i As Integer = 0
   Dim cb As CheckBox
   Dim dgi As DataGridItem
   Dim bookid As Integer
   Dim dr As dsBooks.BooksRow
   For Each dgi In DataGrid1.Items
      cb = CType(dgi.Cells(0).Controls(1), CheckBox)
      If cb.Checked Then
         ' Определить ключ для выбранной записи...
         bookid = CType(DataGrid1.DataKeys(i), Integer)
         ' ...получить указатель на соответствующую запись в наборе данных...
         dr = Me.DsBooks1.Books.FindBybookid(bookid)
         ' ...и удалить эту запись.
         dr.Delete()
      End If
      i += 1
   Next
   Me.SqlDataAdapter1.Update(DsBooks1)
   Me.SqlDataAdapter1.Fill(DsBooks1)
   DataGrid1.DataBind()
End Sub

// C#
private void btnDelete_Click(object sender, System.EventArgs e)
{
   int i = 0;
   CheckBox cb;
   int bookid;
   dsBooks.BooksRow dr;
   foreach(DataGridItem dgi in this.DataGrid1.Items)
   {
      cb = (CheckBox) dgi.Cells[0].Controls[1];
      if(cb.Checked)
      {
         // Определить ключ для выбранной записи...
         bookid = (int) DataGrid1.DataKeys[i];
         // ...получить указатель на соответствующую запись
         // в наборе данных...
         dr = this.dsBooks1.Books.FindBybookid(bookid);
         // ...и удалить эту запись.
         dr.Delete();
      }
      i++;
   }
   this.sqlDataAdapter1.Update(this.dsBooks1);
   this.sqlDataAdapter1.Fill(this.dsBooks1);
   DataGrid1.DataBind();
}

Обратите внимание:

  • чтобы определить, установлен ли флажок, используйте стандартный подход для получения значения элемента управления из столбца-шаблона: получите объект из набора Controls ячейки и приведите его к нужному типу. Если вы получаете значение элемента управления Checkbox, помните, что обычно он - второй элемент управления в ячейке (индекс = 1), поскольку перед ним находится элемент управления Literal (даже если он пуст);
  • при удалении строки нужно задавать по ключу, а не по смещению в наборе данных. Индексы элемента в DataGrid и в таблице могут отличаться. Даже если изначально они совпадали, после удаления первой же строки он станут разными. Показанный выше код извлекает ключ удаляемой записи из набора DataKey сетки. После этого вызывается метод FindBy<ключ> для поиска удаляемых записей;
  • после удаления записей из набора данных (с технической точки зрения, при этом они лишь помечаются для удаления) удалите их из базы данных, вызвав метод Update объекта адаптера данных. Далее код обновляет набор данных информацией из базы данных и заново выполняет привязку сетки к данным.

Модификация сразу нескольких строк

Стандартный способ редактирования строк в DataGrid - добавление к столбцу сетки кнопок Edit, Update и Cancel - позволяет редактировать не более одной строки одновременно. Если пользователю нужно изменить несколько строк, ему придется для каждой строки щелкать кнопку Edit, вносить необходимые изменения и щелкать кнопку Update.

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

Рис. 1

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

Чтобы сконфигурировать сетку для редактирования сразу нескольких строк, добавьте к ней столбцы, как обычно, и преобразуйте все изменяемые столбцы в столбцы-шаблоны. На вкладке Columns окна свойств сетки выберите столбец и щелкните Convert this column into a Template column. Чтобы отредактировать шаблон, щелкните сетку правой кнопкой мыши и выберите Edit Template.

Теперь добавьте к ItemTemplate элементы управления, поддерживающие редактирование. Обратите внимание, что они добавляются не к EditItemTemplate (как это обычно делается), так как иначе строки не будут отображаться в режиме редактирования. То есть элементы управления, поддерживающие редактирование будут находиться в ItemTemplate.

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

DataBinder.Eval(Container, "DataItem.title")

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

Один из способов демонстрируется в следующем примере. Здесь предполагается, что вы используете объект команды (dcmdUpdateBooks), содержащий параметризованный SQL-оператор Update. Код последовательно перебирает все элементы сетки, извлекая значения из элементов управления, поддерживающих редактирование, и присваивая их параметрам команды. После этого выполняется объект команда - по одному разу для каждого элемента сетки.

' Visual Basic
Private Sub btnUpdate_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles btnUpdate.Click
   Dim i As Integer
   Dim dgi As DataGridItem
   Dim bookid As Integer
   Dim TextBoxTitle As TextBox
   Dim CheckBoxInStock As CheckBox
   Dim TextBoxPrice As TextBox
   Dim LabelBookId as Label

   For i = 0 To DataGrid1.Items.Count - 1
       dgi = DataGrid1.Items(i)
       LabelBookId = CType(dgi.Cells(0).Controls(1), Label)
       bookid = CType(LabelBookId.Text, Integer)
       TextBoxTitle = CType(dgi.FindControl("TextBoxTitle"), TextBox)
       CheckBoxInStock = _
           CType(dgi.FindControl("CheckBoxInstock"), CheckBox)
       TextBoxPrice = CType(dgi.FindControl("TextBoxPrice"), TextBox)
       Me.dcmdUpdateBooks.Parameters("@bookid").Value = bookid
       Me.dcmdUpdateBooks.Parameters("@Title").Value = TextBoxTitle.Text
       Me.dcmdUpdateBooks.Parameters("@instock").Value = _
          CheckBoxInStock.Checked
       Me.dcmdUpdateBooks.Parameters("@Price").Value = TextBoxPrice.Text
       Me.SqlConnection1.Open()
       Me.dcmdUpdateBooks.ExecuteNonQuery()
       Me.SqlConnection1.Close()
   Next
End Sub

// C#
private void btnUpdate_Click(object sender, System.EventArgs e)
{
   int i;
   DataGridItem dgi;
   int bookid;
   TextBox TextBoxTitle;
   CheckBox CheckBoxInStock;
   TextBox TextBoxPrice;

   for(i = 0; i <= DataGrid1.Items.Count -1 ; i++)
   {
      dgi = DataGrid1.Items[i];
      Label LabelBookId = (Label) dgi.Cells[0].Controls[1];
      bookid = int.Parse(LabelBookId.Text);
      TextBoxTitle = (TextBox) dgi.FindControl("TextBoxTitle");
      CheckBoxInStock = (CheckBox) dgi.FindControl("CheckBoxInStock");
      TextBoxPrice = (TextBox) dgi.FindControl("TextBoxPrice");
      this.dcmdUpdateBooks.Parameters["@bookid"].Value = bookid;
      this.dcmdUpdateBooks.Parameters["@Title"].Value = TextBoxTitle.Text;
      this.dcmdUpdateBooks.Parameters["@instock"].Value = 
CheckBoxInStock.Checked;
      this.dcmdUpdateBooks.Parameters["@Price"].Value = 
float.Parse(TextBoxPrice.Text);
      this.sqlConnection1.Open();
      this.dcmdUpdateBooks.ExecuteNonQuery();
      this.sqlConnection1.Close();
   }
}

Проверка элементов на наличие изменений

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

Стратегия, пригодная для обоих типов источников данных, - определять наличие изменений в строках, чтобы получить возможность выполнения проверки перед обновлением. Правильный способ реализации такой операции - обработка Changed-события элементов управления в строке. Например, элемент управления TextBox поддерживает событие TextChanged, а флажки - событие CheckedChanged.

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

Допустим, вы хотите реализовать эту стратегию применительно к предыдущему примеру. Тогда создайте объект ArrayList как член класса страницы:

' Visual Basic
Protected bookidlist As ArrayList = New ArrayList()

// C#
protected ArrayList bookidlist = new ArrayList();

Далее создайте обработчик, добавляющий идентификатор книги к объекту ArrayList при каждой модификации значения в каком-либо элементе управления. В следующем коде показан обработчик, который можно вызвать при генерации события TextChanged элемента управления TextBox (или события CheckedChanged элемента управления CheckBox):

' Visual Basic
Protected Sub RowChanged(ByVal sender As Object, _
      ByVal e As System.EventArgs)
   Dim dgi As DataGridItem = _
      CType(CType(sender, Control).NamingContainer, DataGridItem)
   Dim bookidlabel As Label = CType(dgi.Cells(0).Controls(1), Label)
   Dim bookid As Integer = CType(bookidlabel.Text, Integer)
   If Not (bookidlist.Contains(bookid)) Then
      bookidlist.Add(bookid)
   End If
End Sub

// C#
protected void RowChanged( object sender, System.EventArgs e)
{
   DataGridItem dgi = (DataGridItem)(((Control)sender).NamingContainer);
   Label bookidlabel = (Label) dgi.Cells[0].Controls[1];
   int bookid = int.Parse(bookidlabel.Text);
   if (!bookidlist.Contains(bookid))
   {
      bookidlist.Add(bookid);
   }
}

Примечание. Этот метод не должен быть закрытым, иначе привязка к нему впоследствии будет невозможна.

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

А теперь поговорим о методе RowChanged, показанном выше. Код должен извлечь идентификатор книги из текущего элемента. Событие не возвращает этот элемент (в отличие от многих других событий DataGrid), поэтому придется пойти обходным путем. Из аргумента sender события извлеките свойство NamingContainer, которое и представляет нужный элемент сетки. Отсюда можно получить значение элемента управления Label, отображающего идентификатор книги.

Далее нужно проверить, нет ли уже в массиве такого идентификатора. Changed-событие генерируется для каждого элемента управления в строке по отдельности, поэтому, если модифицировано несколько элементов управления, есть вероятность, что один и тот же идентификатор может быть добавлен в массив несколько раз.

Changed-события элементов управления всегда генерируются (и обрабатываются) до событий Click. Поэтому вы можете создать список-массив (array list) в обработчике Changed-события и быть уверенным в том, что он будет доступен к моменту выполнения обработчика Click кнопки, возвращающей форму на сервер (здесь такой обработчик - btnUpdate_Click).

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

' Visual Basic
Private Sub btnUpdate_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles btnUpdate.Click
   Dim i As Integer
   Dim dgi As DataGridItem
    ' Остальные объявления помещаются сюда

   For i = 0 To DataGrid1.Items.Count - 1
       dgi = DataGrid1.Items(i)
       LabelBookId = CType(dgi.Cells(0).Controls(1), Label)
       If bookidlist.Contains(bookid) Then
         TextBoxTitle = CType(dgi.FindControl("TextBoxTitle"), TextBox)
         ' Остальной код обновления помещается сюда
       End If
   Next
End Sub

// C#

private void btnUpdate_Click(object sender, System.EventArgs e)
{
   int i;
   DataGridItem dgi;
   int bookid;
   // Остальные объявления помещаются сюда

   for(i = 0; i <= DataGrid1.Items.Count -1 ; i++)
   {
      dgi = DataGrid1.Items[i];
      TableCell tc = dgi.Cells[0];
      string s = dgi.Cells[0].Text;
      Label LabelBookId = (Label) dgi.Cells[0].Controls[1];
      bookid = int.Parse(LabelBookId.Text);
      if (bookidlist.Contains(bookid)) 
      {
         // Остальной код обновления помещается сюда
      }
   }
}

Осталось последнее: связать обработчики с событиями элементов управления. В Visual Studio это можно только в режиме просмотра HTML-текста. В файле кода страницы (code-behind file) явного создания экземпляров элементов управления не происходит, поэтому они не поддерживаются средствами работы с кодом. Открыв .aspx-файл, переключитесь в режим редактирования HTML-текста и в объявлениях для каждого элемента управления добавьте следующий выделенный текст:

<asp:TemplateColumn HeaderText="title">
   <ItemTemplate>
      <asp:TextBox OnTextChanged="RowChanged"
         id=TextBoxTitle runat="server"
         Text='<%# DataBinder.Eval(Container, "DataItem.title") %>'>
      </asp:TextBox>
   </ItemTemplate>
</asp:TemplateColumn>

<asp:TemplateColumn HeaderText="instock">
   <ItemTemplate>
      <asp:CheckBox id=cbInStock OnCheckedChanged="RowChanged"
        runat="server" 
        Checked='<%# DataBinder.Eval(Container, "DataItem.instock") %>'>
      </asp:CheckBox>
   </ItemTemplate>
</asp:TemplateColumn>

Как TextBox, так и CheckBox могут вызывать один и тот же метод из своих обработчиков Changed-событий, поскольку сигнатуры обоих обработчиков идентичны. То же верно для окна списка или раскрывающегося списка, чье событие SelectedIndexChanged аналогичным образом передает те же аргументы.

Выделение строки щелчком в любом ее месте

По умолчанию модель выбора строк в сетке требует добавления кнопки Select (реально представленной элементом управления LinkButton), чье свойство CommandName задано как Select. Когда пользователь щелкает эту кнопку, элемент управления DataGrid получает команду Select и автоматически отображает строку как выделенную (выбранную).

Однако не всем нравится кнопка Select в явном виде, и часто спрашивают, как сделать так, чтобы пользователь мог выбрать строку в сетке, щелкнув любое ее поле. Решение таково. Добавляем кнопку Select (LinkButton) как обычно. Пользователи могут работать с ней, но можно и скрыть ее. В любом случае к странице добавляется клиентский сценарий, который фактически дублирует функциональность кнопки Select для строки в целом.

Как это делается, показано ниже. В обработчике ItemDataBound сетки прежде всего убедитесь, что вы не находитесь в верхнем или нижнем колонтитуле, либо на полоске прокрутки (pager). Далее получите ссылку на кнопку Select (в этом примере предполагается, что она является первым элементом управления в первой ячейке сетки). После этого можно вызвать малоизвестный метод GetPostBackClientHyperlink. Этот метод возвращает имя функции, возвращающей данные на сервер (postback call), для данного элемента управления. Иными словами, если передать элементу управления LinkButton ссылку, он вернет имя клиентской функции, при вызове которой данные будут возвращены на сервер.

Наконец, назначьте клиентский метод самому элементу. Сетка визуализируется как HTML-таблица. Назначение метода элементу равносильно добавлению к каждой строке таблицы клиентского кода (элемента <TR>). Объект Item сетки не поддерживает назначение ему клиентского кода напрямую, но эту проблему можно обойти, используя его набор Attributes, который передает любой назначенный ему код браузеру.

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

' Visual Basic
Private Sub DataGrid1_ItemDataBound(ByVal sender As Object, _
      ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) _
      Handles DataGrid1.ItemDataBound
   Dim itemType As ListItemType = e.Item.ItemType
   If ((itemType = ListItemType.Pager) Or _
      (itemType = ListItemType.Header) Or _
      (itemType = ListItemType.Footer)) Then
      Return
   Else
      Dim button As LinkButton = _
         CType(e.Item.Cells(0).Controls(0), LinkButton)
      e.Item.Attributes("onclick") = _
         Page.GetPostBackClientHyperlink(button, "")
   End If
End Sub

// C#
private void DataGrid1_ItemDataBound(object sender, 
System.Web.UI.WebControls.DataGridItemEventArgs e)
{
   ListItemType itemType = e.Item.ItemType;
   if ((itemType == ListItemType.Pager) || 
       (itemType == ListItemType.Header) || 
       (itemType == ListItemType.Footer)) 
   {
      return;
   }
   LinkButton button = (LinkButton)e.Item.Cells[0].Controls[0];
   e.Item.Attributes["onclick"] = 
      Page.GetPostBackClientHyperlink(button, "");
}

 


Может пригодится:


Автор: Mike Pope
Прочитано: 6578
Рейтинг:
Оценить: 1 2 3 4 5

Комментарии: (0)

Добавить комментарий
Ваше имя*:
Ваш email:
URL Вашего сайта:
Ваш комментарий*:
Код безопастности*:

Рассылка новостей
Рейтинги
© 2007, Программирование Исходники.Ру