Dynamic Rows in ASP.NET Table with Data Persistance


I've spent a bit of time this week figuring out how to setup an ASP.NET user control that contains a table with a dynamic number of rows.  This turns out to be not so simple if you want to persist the data in the table between postbacks to the server.  Here is how I solved this issue...

Creating dynamic controls in ASP.NET isn't that difficult.  Figuring out how to create them based on user input into the current page, and persisting the data on that page for the dynamic controls is rather problematic.

The solution to this is a combination of techniques including using the ViewState Object as the backing store for the controls properties, and a hundful of overridden methods.

 

Suggested reading:

 

A great article on dynamic control creation by Yuriy Solodkyy can be found here:

 

Dynamically Created Controls in ASP.NET


This article covers the fundementals of creating dynamic controls, explores various methods of doing so, and offers good advice on how to create working dynamic controls depending upon your usage scenario. 

 

A couple of screen shots of the control itself:

 

The user control before a row has been added:

The user control after a couple of rows have been added:

 

A couple of key points:

  • Dynamically created controls MUST be recreated each postback.
  • Recreating the dynamic controls is done in the CreateChildControls() method override.
  • A call to EnsureChildControls() is done inside of the the LoadViewState() method override.

 

The ASPX portion of the user control:

<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="JonoEntry.ascx.vb" Inherits="pmt.ui.JonoEntry" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="atk" %>
<asp:Panel ID="JonoContainer" runat="server">
    <asp:PlaceHolder id="PHJonoTable" runat="server"></asp:PlaceHolder>
    <asp:Button ID="btnAddJono" runat="server" text="Add Jono" />
    <asp:Button ID="btnUpdateTotal" runat="server" Text ="Update" />
</asp:Panel>

 

 

The Code Behind of the user control:

 

Imports System.Globalization

Partial Public Class JonoEntry
    Inherits System.Web.UI.UserControl

    Private Const JONOENTRY_DYNAMICROWCOUNT As String = "JONOENTRY_DRC"
    Public Const JONOENTRY_JONOLIST As String = "JONOENTRYDATA"
    Private Const JONOENTRY_JONOSTOTAL As String = "JONOSTOTAL"


#Region " Overrides "

    Protected Overrides Function SaveViewState() As Object
        Return New Pair(MyBase.SaveViewState(), Nothing)
    End Function

    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
        MyBase.LoadViewState(DirectCast(savedState, Pair).First)

        EnsureChildControls()
    End Sub


    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        MyBase.OnLoad(e)
    End Sub

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()
        InsertJonoTable()
    End Sub


#End Region

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not (Page.IsPostBack) Then
            Me.JONORows = 0
            InsertJonoTable()
        End If


    End Sub
#Region "Properties"
    Protected Property JONOSTotal() As Decimal
        Get
            If Not ViewState(JONOENTRY_JONOSTOTAL) Is Nothing Then
                Return CType(ViewState(JONOENTRY_JONOSTOTAL), Decimal)
            Else
                Dim t As Decimal = 0.0
                Return t
            End If
        End Get
        Set(ByVal value As Decimal)
            ViewState(JONOENTRY_JONOSTOTAL) = value
        End Set
    End Property
    Protected Property JONORowsData() As RBAJono()
        Get
            If Not ViewState(JONOENTRY_JONOLIST) Is Nothing Then
                Return CType(ViewState(JONOENTRY_JONOLIST), RBAJono())
            Else
                Return Nothing
            End If
        End Get
        Set(ByVal value As RBAJono())
            ViewState(JONOENTRY_JONOLIST) = value
        End Set
    End Property


    Protected Property JONORows() As Integer
        Get
            If Not ViewState(JONOENTRY_DYNAMICROWCOUNT) Is Nothing Then
                Return CInt(Fix(ViewState(JONOENTRY_DYNAMICROWCOUNT)))
            Else
                Return 0
            End If
        End Get
        Set(ByVal value As Integer)
            ViewState(JONOENTRY_DYNAMICROWCOUNT) = value
        End Set
    End Property
#End Region

    Protected Function buildTableHeader() As TableHeaderRow

        Dim thr As New TableHeaderRow
        thr.ID = "thr1"
        Dim thc1 As New TableHeaderCell

        Dim thc2 As New TableHeaderCell
        thc2.Text = "Pull From JONO"

        Dim thc3 As New TableHeaderCell
        thc3.Text = "EOR Code"

        Dim thc4 As New TableHeaderCell
        thc4.Text = "Contract Amount Requested"

        thr.Cells.Add(thc1)
        thr.Cells.Add(thc2)
        thr.Cells.Add(thc3)
        thr.Cells.Add(thc4)
        Return thr
    End Function
    Protected Function buildTableFooter(ByVal totalAmount As Decimal) As TableFooterRow
        Dim tfr As New TableFooterRow
        tfr.ID = "tfr1"
        Dim tfc1 As New TableHeaderCell
        Dim tfc2 As New TableHeaderCell
        Dim tfc3 As New TableHeaderCell
        tfc3.HorizontalAlign = HorizontalAlign.Right
        tfc3.Text = "Total:"

        Dim tfc4 As New TableHeaderCell
        tfc4.Text = Format(totalAmount.ToString(), "Currency")
        tfr.Cells.Add(tfc1)
        tfr.Cells.Add(tfc2)
        tfr.Cells.Add(tfc3)
        tfr.Cells.Add(tfc4)
        Return tfr
    End Function

    Protected Sub InsertJonoTable()

        Dim rowCount As Integer = Me.JONORows

        'Create the Table and Add it to the Page
        Dim jonoTable As New Table
        jonoTable.ID = "jonoTable"
        jonoTable.Style("width") = "100%"
        'Add the table to the page

        jonoTable.Rows.Add(buildTableHeader)
        Dim totalAmount As Decimal = Me.JONOSTotal

        If (rowCount > 0) Then
            ' Add the dynamic rows
            For currentRow = 1 To rowCount
                Dim r As TableRow
                r = AddNewJonoRow(currentRow)
                jonoTable.Rows.Add(r)
            Next
        End If

        'Add the footer rows
        jonoTable.Rows.Add(buildTableFooter(totalAmount))
        PHJonoTable.Controls.Clear()
        PHJonoTable.Controls.Add(jonoTable)

    End Sub
    Protected Function AddNewJonoRow(ByVal RowPosition As Integer) As TableRow
        Dim r As New TableRow
        Dim c1 As New TableCell

        r.ID = "RBAJONO_" + RowPosition.ToString()
        Dim c2 As New TableCell
        Dim tbJono As New TextBox
        tbJono.ID = "tbJono_" + RowPosition.ToString
        tbJono.Style.Add("Width", "100%")
        c2.Controls.Add(tbJono)

        Dim c3 As New TableCell
        Dim tbEORCode As New TextBox
        tbEORCode.ID = "tbEORCode_" + RowPosition.ToString
        tbEORCode.Style.Add("Width", "100%")
        c3.Controls.Add(tbEORCode)

        Dim c4 As New TableCell
        Dim tbAmount As New TextBox
        tbAmount.ID = "tbAmount_" + RowPosition.ToString
        tbAmount.Style.Add("Width", "100%")
        c4.Controls.Add(tbAmount)

        r.Cells.Add(c1)
        r.Cells.Add(c2)
        r.Cells.Add(c3)
        r.Cells.Add(c4)

        Return r
    End Function

    Protected Sub btnAddJONO_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnAddJono.Click

        saveCurrentData()
        Dim rowCount As Integer = Me.JONORows
        rowCount = rowCount + 1
        Me.JONORows = rowCount
        InsertJonoTable()
        restoreCurrentData()

    End Sub

    Protected Sub btnUpdateTotal_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnUpdateTotal.Click

        saveCurrentData()
        InsertJonoTable()
        restoreCurrentData()
    End Sub


    Public Sub saveCurrentData()

        Dim Result As Decimal = 0
        Dim rowCount As Integer = 0
        Dim RBAJonoList As New List(Of RBAJono)
        Dim t As Table = PHJonoTable.FindControl("jonoTable")
        If Not (t Is Nothing) Then
            For Each row As TableRow In t.Rows
                Dim r As New RBAJono
                If (row.ID.Contains("RBAJONO_")) Then
                    rowCount = rowCount + 1
                    r.Row = rowCount
                    For Each cell As TableCell In row.Cells
                        If (cell.Controls.Count > 0) Then
                            If TypeOf cell.Controls(0) Is TextBox Then
                                If cell.Controls(0).ID.Contains("tbJono_") Then
                                    r.JonoId = DirectCast(cell.Controls(0), TextBox).Text
                                End If

                                If cell.Controls(0).ID.Contains("tbEORCode_") Then
                                    r.EorCode = DirectCast(cell.Controls(0), TextBox).Text
                                End If

                                If cell.Controls(0).ID.Contains("tbAmount_") Then
                                    r.Amount = DirectCast(cell.Controls(0), TextBox).Text
                                    If (r.Amount.Length > 0) Then

                                        Dim ci As New CultureInfo("en-US")
                                        Result = Result + Decimal.Parse(r.Amount, NumberStyles.Currency, ci.NumberFormat)

                                    End If
                                End If

                            End If

                        End If
                    Next
                    RBAJonoList.Add(r)
                End If
            Next

        End If
        Me.JONORowsData = RBAJonoList.ToArray()
        Me.JONOSTotal = Result
    End Sub
    Public Function getJonoData() As RBAJonoList
        Dim RBAJonoList As List(Of RBAJono) = Me.JONORowsData.ToList()
        Return RBAJonoList
    End Function

    Protected Sub restoreCurrentData()

        Dim rowCount As Integer = 0
        Dim RBAJonoList As List(Of RBAJono) = Me.JONORowsData.ToList()
        If (RBAJonoList.Count > 0) Then
            Dim t As Table = PHJonoTable.FindControl("jonoTable")
            If Not (t Is Nothing) Then
                For Each row As TableRow In t.Rows

                    If (row.ID.Contains("RBAJONO_")) Then
                        If (RBAJonoList.Count > rowCount) Then ' Make sure we have enough data
                            If Not (RBAJonoList(rowCount) Is Nothing) Then ' Make sure the data isn't NULL
                                Dim r As RBAJono = RBAJonoList(rowCount)
                                rowCount = rowCount + 1

                                For Each cell As TableCell In row.Cells
                                    If (cell.Controls.Count > 0) Then
                                        If TypeOf cell.Controls(0) Is TextBox Then
                                            If cell.Controls(0).ID.Contains("tbJono_") Then
                                                DirectCast(cell.Controls(0), TextBox).Text = r.JonoId
                                            End If

                                            If cell.Controls(0).ID.Contains("tbEORCode_") Then
                                                DirectCast(cell.Controls(0), TextBox).Text = r.EorCode
                                            End If

                                            If cell.Controls(0).ID.Contains("tbAmount_") Then
                                                DirectCast(cell.Controls(0), TextBox).Text = r.Amount
                                            End If

                                        End If

                                    End If
                                Next
                            End If
                        End If
                    End If
                Next

            End If

        End If

    End Sub

End Class

 

  • Currently 0.00/5
Rating: 0.00/5 (0 votes cast)

Share It!

Trackback

Trackback URL for this entry: http://www.tbayne.net/trackback.php?id=20110524142230277

No trackback comments for this entry.