问题
I have a databound gridview that I create dynamically. The datasource returns an object based on the several DropDownLists (types of objects are different). Depending on the type of the object, the GridView must display certain fields specific only for the object. Also, there is a DropDownList whose SelectedValue decides which columns of an object will be added/excluded from the GridView.
Here is a method that creates the GridView (I write in VB.NET but C# is also very welcome):
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
The GridView is always in Edit mode, meaning, it's ItemTemplate is a TexBox/DropDownList/CheckBox. Here is an ITemplate Class:
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
The DropDownLists are bound to their own ObjectDataSources and are created in the mark-up.
As I understand, the GridView must be instantiated on every postback in the Page.Init. The Page.Load is to late for creating the gridview as it will not maintain the ViewState and will be impossible to update. However, when I create it on Init, the DropDownLists are not created or DataBound yet, so there is no selected value. I tried populating the DropDownLists on their Inits instead of bining them to the DataSource but it throws a ViewState error when I change the SelectedValue.
When I create the GridView on Load, everything works perfectly, except the most important part, updating...
Can anyone help me with figuring out where do I initialize/bind the GridView/DropDownLists? I have been struggling with this for three days now, getting desperate here :(
回答1:
Did not find a solution but found a workaround.
The GridView is created OnInit, fields are added on GridView Init. After all of the DropDownLists are DataBound, the ObjectDataSource receives its parameters from the DropDownLists.
There is one DropDownList, which is responsible for the type of Object returned. After I change the SelectedValue, the DropDownList is reinitiated and ObjectDataSource still has the old value and returns the wrong object. This is where the error was - binding GridView with an Object with wrong fields. Instead of this, I used QueryString and did a postback. On the next load, the ObjectDataSource returns the correct object that matches GridView's fields. Everything is smooth from there.
来源:https://stackoverflow.com/questions/15912247/ddynamic-gridview-with-templatefields-confusion-in-lifecycle