Ddynamic GridView с TemplateFields - путаница в LifeCycle
У меня есть сетка с привязкой к данным, которую я создаю динамически. Источник данных возвращает объект, основанный на нескольких DropDownLists (типы объектов различны). В зависимости от типа объекта GridView должен отображать определенные поля, относящиеся только к объекту. Также существует DropDownList, SelectedValue которого определяет, какие столбцы объекта будут добавлены / исключены из GridView.
Вот метод, который создает GridView (я пишу на VB.NET, но C# также очень приветствуется):
Private Sub CreateGridView()
Dim gv As New GridView
With gv
.AllowSorting = True
.AutoGenerateColumns = False
.CssClass = "gv"
.EmptyDataText = "The list is empty"
.ID = "gv"
.ShowFooter = True
.AlternatingRowStyle.Wrap = False
.EditRowStyle.Wrap = False
.FooterStyle.Wrap = False
.HeaderStyle.Wrap = False
.SortedAscendingCellStyle.CssClass = "sortAscCell"
.SortedAscendingHeaderStyle.CssClass = "sortAscHeader"
.SortedDescendingCellStyle.CssClass = "sortDescCell"
.SortedDescendingHeaderStyle.CssClass = "sortDescHeader"
AddHandler .RowDataBound, AddressOf gv_RowDataBound
AddHandler .DataBound, AddressOf gv_DataBound
AddHandler .RowUpdating, AddressOf gv_RowUpdating
.DataSource = odsEquipment.Select
.DataKeyNames = {"equipmentID"}
End With
For Each item As Dictionary In odsDictionary.Select
If ddlStages.SelectedValue <> "" Then
If ddlStages.SelectedValue >= item.stage_id Then
Dim tf As New TemplateField()
tf.SortExpression = item.col
tf.HeaderTemplate = New GridViewTemplate(item.title, item.col)
tf.ItemTemplate = New GridViewTemplate(DataControlRowType.DataRow, item.col, item.ctrlType, item.length)
tf.FooterTemplate = New GridViewTemplate(DataControlRowType.Footer, item.col, item.ctrlType, item.length)
gv.Columns.Add(tf)
End If
End If
Next
gv.DataBind()
divGV.Controls.Add(gv)
End Sub
GridView всегда находится в режиме редактирования, то есть ItemTemplate представляет собой TexBox/DropDownList/CheckBox. Вот класс ITemplate:
Imports System.Data
Public Class GridViewTemplate
Implements ITemplate
Private templateType As DataControlRowType
Private title As String
Private columnBinding As String
Private ctrlType As String
Private length As Integer
Public Sub New(ByVal vTitle As String, vColumnBinding As String)
templateType = DataControlRowType.Header
title = vTitle
columnBinding = vColumnBinding
End Sub
Public Sub New(ByVal type As DataControlRowType, ByVal vColumnBinding As String, ByVal vCtrlType As String, vLength As Integer)
templateType = type
columnBinding = vColumnBinding
ctrlType = vCtrlType
length = vLength
End Sub
Private Sub InstantiateIn(container As Control) Implements ITemplate.InstantiateIn
Select Case templateType
Case DataControlRowType.Header
Dim lb As New LinkButton()
lb.ID = "lb" + columnBinding
lb.CommandName = "Sort"
lb.CommandArgument = columnBinding
lb.Text = title
container.Controls.Add(lb)
Exit Select
Case DataControlRowType.DataRow
If ctrlType = "Label" Then
Dim lbl = New Label()
lbl.ID = "lbl" + columnBinding
AddControl(lbl, container)
ElseIf ctrlType = "TextBox" Then
Dim tb As New TextBox
tb.ID = "tb" + columnBinding
tb.MaxLength = length
AddControl(tb, container)
ElseIf ctrlType = "CheckBox" Then
Dim cb = New CheckBox()
cb.ID = "cb" + columnBinding
AddControl(cb, container)
ElseIf ctrlType = "DropDownList" Then
Dim ddl = New DropDownList()
ddl.ID = "ddl" + columnBinding
AddControl(ddl, container)
End If
Exit Select
Case DataControlRowType.Footer
If ctrlType = "Label" Then
Dim tbFrom As New TextBox()
tbFrom.ID = "tb" + columnBinding + "From"
container.Controls.Add(tbFrom)
Dim tbTo As New TextBox()
tbTo.ID = "tb" + columnBinding + "From"
container.Controls.Add(tbTo)
ElseIf ctrlType = "TextBox" Then
Dim tb As New TextBox
tb.ID = "tb" + columnBinding
tb.MaxLength = length
container.Controls.Add(tb)
ElseIf ctrlType = "CheckBox" Then
Dim cb = New CheckBox()
cb.ID = "cb" + columnBinding
container.Controls.Add(cb)
ElseIf ctrlType = "DropDownList" Then
Dim ddl = New DropDownList()
ddl.ID = "ddl" + columnBinding
AddControl(ddl, container)
End If
Exit Select
Case Else
Exit Select
End Select
End Sub
Private Sub AddControl(ctrl As Control, container As Control)
AddHandler ctrl.DataBinding, AddressOf OnDataBinding
container.Controls.Add(ctrl)
End Sub
Private Sub OnDataBinding(ByVal sender As Object, ByVal e As EventArgs)
If sender.GetType = GetType(Label) Then
Dim lb As Label = DirectCast(sender, Label)
Dim container As GridViewRow = DirectCast(lb.NamingContainer, GridViewRow)
lb.Text = DataBinder.Eval(container.DataItem, columnBinding).ToString
ElseIf sender.GetType = GetType(TextBox) Then
Dim tb As TextBox = DirectCast(sender, TextBox)
Dim container As GridViewRow = DirectCast(tb.NamingContainer, GridViewRow)
tb.Text = DataBinder.Eval(container.DataItem, columnBinding).ToString
ElseIf sender.GetType = GetType(CheckBox) Then
Dim cb As CheckBox = DirectCast(sender, CheckBox)
Dim container As GridViewRow = DirectCast(cb.NamingContainer, GridViewRow)
cb.Checked = DataBinder.Eval(container.DataItem, columnBinding).ToString
ElseIf sender.GetType = GetType(DropDownList) Then
Dim ddl As DropDownList = DirectCast(sender, DropDownList)
Dim container As GridViewRow = DirectCast(ddl.NamingContainer, GridViewRow)
If columnBinding = "criticalityRating" Then
ddl.Items.Add("")
For i = 1 To 4
ddl.Items.Add(i)
Next
ElseIf columnBinding = "property_id" Then
For Each p As PropertyMP In PropertyMPDB.GetProperties
ddl.Items.Add(New ListItem(p.propertyMP, p.property_id))
Next
End If
If templateType = DataControlRowType.DataRow Then
ddl.SelectedValue = DataBinder.Eval(container.DataItem, columnBinding).ToString
End If
End If
End Sub
End Class
DropDownLists связаны с их собственными ObjectDataSources и создаются в разметке.
Как я понимаю, GridView должен создаваться при каждой обратной передаче в Page.Init. Page.Load слишком поздно для создания вида сетки, так как он не будет поддерживать ViewState и будет невозможно обновить. Тем не менее, когда я создаю его в Init, DropDownLists еще не созданы или DataBound еще, поэтому нет выбранного значения. Я попытался заполнить DropDownLists на их Inits вместо привязки их к DataSource, но он выдает ошибку ViewState при изменении SelectedValue.
Когда я создаю GridView on Load, все работает отлично, кроме самой важной части, обновления...
Может кто-нибудь помочь мне выяснить, где я могу инициализировать / связать GridView / DropDownLists? Я боролся с этим в течение трех дней, отчаявшись здесь:(
1 ответ
Не нашел решения, но нашел обходной путь.
GridView создается OnInit, поля добавляются в GridView Init. После того как все DropDownLists имеют DataBound, ObjectDataSource получает свои параметры из DropDownLists.
Существует один DropDownList, который отвечает за тип возвращаемого объекта. После того, как я изменил SelectedValue, DropDownList повторно инициируется, и ObjectDataSource все еще имеет старое значение и возвращает неправильный объект. Вот где была ошибка - связывание GridView с объектом с неправильными полями. Вместо этого я использовал QueryString и сделал обратную передачу. При следующей загрузке ObjectDataSource возвращает правильный объект, который соответствует полям GridView. Оттуда все гладко.