1

I'm a tad confused. I have a Repeater which populates a list of Checkbox controls and Label controls. But it doesn't seem like the checked state of the checkboxes is remembered in ViewState until AFTER the first postback.

Scenario: I have 5 items in my custom checkbox list. I select the first 3 and submit the form. The first 3 are no longer selected. I select items 1, 3 and 5 and submit again. After the page is loaded 1, 3 and 5 are still selected.

Here's the entire code for the test page I'm using. My apologies for the VB :-p

Imports System.Xml

Partial Public Class _Default
    Inherits System.Web.UI.Page

    Dim _roles As Repeater
    Dim _output As Literal

    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
        _roles = New Repeater()
        _roles.ItemTemplate = New RolesListTemplate(ListItemType.Item)
        Me.Form.Controls.Add(_roles)

        Dim btn As New Button()
        btn.Text = "Save"
        btn.UseSubmitBehavior = True
        AddHandler btn.Command, AddressOf btnSave_OnCommand
        Me.Form.Controls.Add(btn)

        _output = New Literal
        Me.Form.Controls.Add(_output)
    End Sub

    Private Sub btnSave_OnCommand(ByVal sender As Object, ByVal e As CommandEventArgs)
        Dim roleData As String = GetXML(_roles)
        _output.Text = "<br>" & HttpUtility.HtmlEncode(roleData) & "<br>DONE"
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        If Not Page.IsPostBack Then
            Dim RoleData As New List(Of Role)

            RoleData.Add(New Role With {.selected = 0, .enabled_ind = 1, .role_id = 123, .role_description = "Role 123"})
            RoleData.Add(New Role With {.selected = 0, .enabled_ind = 1, .role_id = 345, .role_description = "Role 345"})
            RoleData.Add(New Role With {.selected = 0, .enabled_ind = 1, .role_id = 678, .role_description = "Role 678"})
            RoleData.Add(New Role With {.selected = 0, .enabled_ind = 1, .role_id = 6987, .role_description = "Role 6987"})
            RoleData.Add(New Role With {.selected = 0, .enabled_ind = 1, .role_id = 1122, .role_description = "Role 1122"})

            If RoleData IsNot Nothing Then
                If RoleData.Count > 0 Then
                    _roles.DataSource = RoleData
                    _roles.DataBind()
                    _roles.Visible = True
                Else
                    _roles.Visible = False
                End If
            End If
        End If

    End Sub

    Private Function GetXML(ByVal _cb_roles As Repeater) As String

        Dim settings As New XmlWriterSettings
        settings.CheckCharacters = True
        settings.CloseOutput = True
        settings.OmitXmlDeclaration = True

        Dim xw As XmlWriter
        Dim sb As New StringBuilder()
        xw = XmlWriter.Create(sb, settings)

        xw.WriteStartDocument()
        xw.WriteStartElement("roles")

        For Each row As RepeaterItem In _cb_roles.Items
            Dim pnl As Panel = TryCast(row.Controls.Item(0), Panel)
            Dim cb As CheckBox = TryCast(pnl.Controls.Item(0), CheckBox)
            Dim id As String = String.Empty
            Dim parts() As String = cb.ID.Split("_"c)
            id = cb.InputAttributes("role_id")
            xw.WriteStartElement("role")
            xw.WriteAttributeString("role_id", id)
            If cb.Checked Then
                xw.WriteAttributeString("selected", "1")
            Else
                xw.WriteAttributeString("selected", "0")
            End If
            xw.WriteEndElement()

        Next

        xw.WriteEndElement()
        xw.WriteEndDocument()
        xw.Close()

        Return sb.ToString
    End Function

    Public Class RolesListTemplate
        Implements ITemplate, INamingContainer

        Private _ltItemType As ListItemType
        Private _ctlParent As WebControl

        Sub New(ByVal pType As ListItemType)
            Try
                _ltItemType = pType
            Catch ex As Exception
                Throw ex
            End Try
        End Sub

        Public Sub InstantiateIn(ByVal container As System.Web.UI.Control) Implements System.Web.UI.ITemplate.InstantiateIn

            Select Case _ltItemType

                Case ListItemType.Header
                Case ListItemType.Item

                    Dim pnl As New Panel()

                    Dim cb As New CheckBox()
                    cb.AutoPostBack = True
                    cb.ID = "role_check"
                    AddHandler cb.DataBinding, AddressOf RolesListTemplate_DataBind
                    pnl.Controls.Add(cb)

                    Dim lbl As New Label()
                    lbl.ID = "role_lbl"
                    AddHandler lbl.DataBinding, AddressOf RolesListTemplate_DataBind
                    pnl.Controls.Add(lbl)

                    container.Controls.Add(pnl)

                Case ListItemType.AlternatingItem
                Case ListItemType.Footer

            End Select

        End Sub

        Private Sub RolesListTemplate_DataBind(ByVal sender As Object, ByVal e As System.EventArgs)

            Dim _sender As Control = DirectCast(sender, Control)
            Dim container As RepeaterItem = DirectCast(_sender.NamingContainer, RepeaterItem)

            Dim role As Role = TryCast(container.DataItem, Role)
            If role IsNot Nothing Then

                If role.selected OrElse role.enabled_ind Then
                    Select Case _sender.ID
                        Case "role_check"
                            Dim role_check As CheckBox = DirectCast(_sender, CheckBox)
                            role_check.Checked = role.selected
                            role_check.ID = "role_list_" & role.role_id.ToString()
                            role_check.InputAttributes("role_id") = role.role_id.ToString()

                        Case "role_lbl"
                            Dim role_lbl As Label = DirectCast(_sender, Label)
                            role_lbl.Text = role.role_description
                    End Select
                Else
                    container.Visible = False
                End If

            End If

        End Sub

    End Class

    Public Class Role
        Public enabled_ind As Boolean
        Public selected As Boolean
        Public role_id As Integer
        Public role_description As String
    End Class

End Class
2
  • 1
    No need to apologize for the VB. Commented Jul 29, 2010 at 20:17
  • It was intended to be directed at the fact that the VB code looks like junk in the syntax highlighting. Commented Jul 29, 2010 at 20:21

1 Answer 1

2

This is a very common mistake. You want to call DataBind in the Init event, even for post backs.

Sign up to request clarification or add additional context in comments.

4 Comments

Aah... so, I have to make my database calls on ever page load? It's a low traffic page, so the database impact is minimal, but seems kinda sloppy to me.
@Wes - It is unfortunate but Web Forms has always been about rapid development over efficiency.
Dynamic controls are a bit sloppy. Chances are that you can restructure your code to bind the data to a repeater on the page instead of adding the repeater to a container via code.
@ScottE - While I agree in principle, in practice these CRUD type pages work just fine with the dynamic controls.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.