Skip to content
June 4, 2010 / pauldundon

Dynamic Templates and Postback in FormView and GridView

.NET makes it possible to use dynamic templates (ie, classes implementing the ITemplate interface) with both FormView and GridView. However, these templates are not recreated automatically on postback, so that any values entered into controls by the user will be lost unless you restore the templates explicitly during the page Init phase.

This isn’t particularly onerous if your FormView or GridView is declared as part of the page markup and so is created as part of the Init phase. However, if you add the FormView or GridView dynamically, there’s no obvious point at which to initialise the templates.

One way to fix this is to derive classes from FormView and GridView and override LoadViewstate and SaveViewstate to persist information about the templates. The following code is my attempt at this; it can certainly be improved. Note that it allows for the templates to implement IStateManager, in which case, they can persist their own state as  part of the operation.

Public Class TemplateableGridView
    Inherits GridView

    Protected Overrides Function SaveViewState() As Object
        ' A template field might not have any state of its own, so might not appear
        ' in the viewstate unless we force the grid view to save every column which
        ' it will do if we mark the viewstate as dirty
        Columns.SetDirty()

        Dim baseState As Object = MyBase.SaveViewState()
        Dim i As Integer = 0
        Dim columnState As New List(Of Triplet)
        For Each dcf As DataControlField In Columns
            If TypeOf (dcf) Is TemplateField Then
                Dim tf As TemplateField = dcf
                Dim state As Object = Nothing
                If TypeOf (tf.ItemTemplate) Is IStateManager Then
                    state = CType(tf.ItemTemplate, IStateManager).SaveViewState
                End If
                Dim t As New Triplet( _
                    i, _
                    TemplateCreateHelper.SafeGetActivatorTypeName(tf.ItemTemplate), _
                    state)
                columnState.Add(t)
            End If
            i += 1
        Next
        Return New Pair(baseState, IIf(columnState.Count = 0, Nothing, columnState))
    End Function

    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
        Dim ss As Pair = savedState

        MyBase.LoadViewState(ss.First)
        If Not ss.Second Is Nothing Then
            For Each t As Triplet In ss.Second
                Dim dcf As TemplateField = Columns(t.First)
                Dim template As Object = TemplateCreateHelper.SafeCreateInstance(t.Second)
                If TypeOf template Is IStateManager Then
                    CType(template, IStateManager).LoadViewState(t.Third)
                End If
                dcf.ItemTemplate = template
            Next
        End If

    End Sub
End Class

Public Class TemplateableFormView
    Inherits FormView

    Protected Function GetTemplateState(ByVal template As ITemplate)
        Dim p As New Pair(TemplateCreateHelper.SafeGetActivatorTypeName(template), Nothing)
        If Not template Is Nothing AndAlso TypeOf (template) Is IStateManager Then
            p.Second = CType(template, IStateManager).SaveViewState
        End If
        Return p
    End Function

    Protected Overrides Function SaveViewState() As Object
        Dim baseState As Object = MyBase.SaveViewState()
        Dim pairs As New List(Of Pair)
        pairs.Add(GetTemplateState(ItemTemplate))
        pairs.Add(GetTemplateState(InsertItemTemplate))
        pairs.Add(GetTemplateState(EditItemTemplate))

        Return New Pair(baseState, pairs)
    End Function

    Protected Function RecreateTemplate(ByVal state As Pair) As ITemplate
        Dim template As ITemplate = TemplateCreateHelper.SafeCreateInstance(state.First)
        If Not template Is Nothing AndAlso TypeOf (template) Is IStateManager Then
            CType(template, IStateManager).LoadViewState(state.Second)
        End If
        Return template
    End Function

    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
        Dim ss As Pair = savedState

        MyBase.LoadViewState(ss.First)

        Dim pairs As List(Of Pair) = ss.Second
        ItemTemplate = RecreateTemplate(pairs(0))
        InsertItemTemplate = RecreateTemplate(pairs(1))
        EditItemTemplate = RecreateTemplate(pairs(2))

    End Sub
End Class

Public Class TemplateCreateHelper
    Public Shared Function SafeGetActivatorTypeName(ByVal item As Object) As String
        If item Is Nothing Then
            Return Nothing
        Else
            Return item.GetType.FullName
        End If
    End Function
    Public Shared Function SafeCreateInstance(ByVal typeName As String) As Object
        If typeName = "" Then
            Return Nothing
        Else
            Return Activator.CreateInstance(Type.GetType(typeName))
        End If
    End Function
End Class
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: