Home All Groups Group Topic Archive Search About

Problem creating TemplateFields in DetailsView dynamically

Author
26 Nov 2007 9:21 AM
Stefan Dirks
Hello,

I want to create a TempalteField in a DetailsView dynamically. I call the
"SetDetailsTemplateFields"-Method on every Postback (Page_OnLoad).

The Problem that occurs is, that on every Postback I get a new row, but the
TemplateField is only created in the last row.

Example:

ID            10

Name       Smith

Memo      [               ]   <-   TemplateField

After Postback:

ID            10

Name       Smith

Memo

Memo      [               ]   <-   TemplateField

When I try to update the DetailsView I get an ArgumentOutOfRangeException.

How can I make sure, that only one row is created for every TemplateField?

Thanks and regards,

Stefan



    Public Class AddTemplateToDetailsView

        Implements ITemplate


        Private m_ListItemType As ListItemType
        Private m_Field As New DetailField



        Public Sub New(ByVal _listItemType As ListItemType, ByVal _field As
DetailField)

            m_ListItemType = _listItemType
            m_Field = _field

        End Sub


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

            Select Case m_ListItemType

               Case ListItemType.Item

                    Select Case m_Field.ItemType.ToLower()

                        Case "textbox_multi"

                            Dim tb As New TextBox
                            tb.Rows = 5
                            tb.TextMode = TextBoxMode.MultiLine
                            tb.Text = "Test"

                            container.Controls.Add(tb)

                        Case Else

                    End Select

                Case Else


                    Throw New Exception("Unknown ListItemType.")

            End Select

        End Sub



        Public Sub SetDetailsTemplateFields(ByRef _dv As
System.Web.UI.WebControls.DetailsView, ByVal _dvdId As String)

            Dim oDBSqlClient As New DBSqlClient()
            Dim dvFields(0) As DetailField

            dvFields = oDBSqlClient.GetDetailViewFields(_dvdId)

            Dim counter As Integer = 0

            For Each dvField As DetailField In dvFields

                If counter <> 0 Then

                         ReDim Preserve dvFields(counter)

                End If


                Select Case dvField.Type.ToLower()


                    Case "template"

                        Dim tf As TemplateField = New TemplateField()

                        If Not String.IsNullOrEmpty(dvField.ItemType) Then
                            tf.ItemTemplate = New
AddTemplateToDetailsView(ListItemType.Item, dvField)
                        End If
                        If Not String.IsNullOrEmpty(dvField.EditType) Then
                            tf.EditItemTemplate = New
AddTemplateToDetailsView(ListItemType.EditItem, dvField)
                        End If
                        If Not String.IsNullOrEmpty(dvField.InsertType) Then
                            tf.InsertItemTemplate = New
AddTemplateToDetailsView(ListItemType.SelectedItem, dvField)

                        End If

                        tf.HeaderText = dvField.HeaderText


                        _dv.Fields.Add(tf)


                End Select

                counter += 1

            Next

        End Sub

Author
27 Nov 2007 9:19 AM
Walter Wang [MSFT]
Hi Stefan,

This is a quick note to let you know that I am performing research on this
issue and will get back to you as soon as possible. I appreciate your
patience.


Regards,
Walter Wang (waw***@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
Are all your drivers up to date? click for free checkup

Author
27 Nov 2007 3:58 PM
Stefan Dirks
Hi Walter,

thank you very much for your answer.

The problem is just solved. The fields "id" and "name" are BoundFields. I
now clear the entire DetailsView-Fields and create the bound and
templatefields on every postback. Is there another way to handle this?

I have another question concerning the databinding of the TemplateField. I
use a SqlDataSource. The BoundFields have the DataValue-Property to bind the
Update-/Insert-Parameter to the SqlDataSource. How can I bind a
TemplateField to the SqlDataSource? When I try to update a dataset I allways
get the error-message, that the scalar-variable is not declared.

Thanks and regards,

Stefan

""Walter Wang [MSFT]"" <waw***@online.microsoft.com> schrieb im Newsbeitrag
Show quoteHide quote
news:KjKD7aNMIHA.5204@TK2MSFTNGHUB02.phx.gbl...
> Hi Stefan,
>
> This is a quick note to let you know that I am performing research on this
> issue and will get back to you as soon as possible. I appreciate your
> patience.
>
>
> Regards,
> Walter Wang (waw***@online.microsoft.com, remove 'online.')
> Microsoft Online Community Support
>
> ==================================================
> When responding to posts, please "Reply to Group" via your newsreader so
> that others may learn and benefit from your issue.
> ==================================================
>
> This posting is provided "AS IS" with no warranties, and confers no
> rights.
>
Author
29 Nov 2007 2:13 AM
Walter Wang [MSFT]
Hi Stefan,

Thanks very much for your update.

I have done some research and I think we need to use another approach to
create dynamic TemplateField at runtime.

Apparently the added field's name is saved in ViewState, but ItemTemplate
and EditItemTemplate is not, that's why you will see duplicate rows in your
first message. On the other hand, if we only create the new TemplateField
upon page's first load (Not Me.IsPostBack) and set the ItemTemplate and
EditItemTemplate every time, we cannot extract the values from the
EditItemTemplate when updating, due to some internal implementation detail
of DetailsView.

This turns out not a trivial task to do. We need to create all three types
of classes here:

* a customized DetailsView class to override CreateFieldSet and add your
dynamic TemplateField
* a customized TemplateField class to override the ExtractValuesFromCell
method to return the data from its template, which is needed by the data
layer
* customized ItemTemplate and EditItemTemplate

I'm include the complete code listing here for your reference (I'm using
the Northwind sample database from SqlServer):

Imports System
Imports System.Web
Imports System.Data
Imports System.Web.UI
Imports System.Web.UI.WebControls

Public Class MyStringItemTemplate
    Implements ITemplate

    Private m_column As String
    Public Sub New(ByVal column As String)
        m_column = column
    End Sub

    Public Sub InstantiateIn(ByVal container As System.Web.UI.Control)
Implements System.Web.UI.ITemplate.InstantiateIn
        Dim l As New Literal()
        AddHandler l.DataBinding, AddressOf BindData
        container.Controls.Add(l)
    End Sub

    Protected Sub BindData(ByVal sender As Object, ByVal e As EventArgs)
        Dim l As Literal = sender
        Dim dv As DetailsView = l.NamingContainer
        l.Text = DataBinder.Eval(dv.DataItem, m_column)
    End Sub
End Class

Public Class MyStringEditItemTemplate
    Implements ITemplate

    Private m_column As String
    Public Sub New(ByVal column As String)
        m_column = column
    End Sub
    Public Sub InstantiateIn(ByVal container As System.Web.UI.Control)
Implements System.Web.UI.ITemplate.InstantiateIn
        Dim tb As New TextBox()
        tb.ID = m_column
        AddHandler tb.DataBinding, AddressOf BindData
        container.Controls.Add(tb)
    End Sub
    Protected Sub BindData(ByVal sender As Object, ByVal e As EventArgs)
        Dim tb As TextBox = sender
        Dim dv As DetailsView = tb.NamingContainer
        tb.Text = DataBinder.Eval(dv.DataItem, m_column)
    End Sub
End Class

Public Class MyTemplateField
    Inherits TemplateField

    Private m_column As String
    Public Sub New(ByVal column As String)
        ItemTemplate = New MyStringItemTemplate(column)
        EditItemTemplate = New MyStringEditItemTemplate(column)
        m_column = column
    End Sub

    Public Overrides Sub ExtractValuesFromCell(ByVal dictionary As
System.Collections.Specialized.IOrderedDictionary, ByVal cell As
System.Web.UI.WebControls.DataControlFieldCell, ByVal rowState As
System.Web.UI.WebControls.DataControlRowState, ByVal includeReadOnly As
Boolean)
        MyBase.ExtractValuesFromCell(dictionary, cell, rowState,
includeReadOnly)
        Dim tb As TextBox = cell.FindControl(m_column)
        dictionary(m_column) = tb.Text
    End Sub
End Class

Public Class MyDetailsView
    Inherits DetailsView

    Protected Overrides Function CreateFieldSet(ByVal dataItem As Object,
ByVal useDataSource As Boolean) As System.Collections.ICollection
        Dim col As New MyTemplateField("Description")

        Dim al As New ArrayList()
        For Each f As DataControlField In MyBase.CreateFieldSet(dataItem,
useDataSource)
            al.Add(f)
        Next
        al.Add(col)
        Return al
    End Function
End Class



<%@ Page Language="vb" AutoEventWireup="false"
CodeBehind="WebForm2.aspx.vb" Inherits="WebApplication2.WebForm2" %>

<%@ Register assembly="WebApplication2" namespace="WebApplication2"
tagprefix="cc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

        <cc1:MyDetailsView ID="MyDetailsView1" runat="server"
AutoGenerateRows="False"
         AutoGenerateEditButton="true" DataKeyNames="CategoryID"
            DataSourceID="SqlDataSource1">
            <Fields>
                <asp:BoundField DataField="CategoryID" HeaderText="ID"
ReadOnly="True"
                    SortExpression="CategoryID" />
                <asp:BoundField DataField="CategoryName" HeaderText="Name"
                    SortExpression="CategoryName" />
            </Fields>
        </cc1:MyDetailsView>
        <asp:SqlDataSource ID="SqlDataSource1" runat="server"
            ConnectionString="<%$
ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="SELECT [CategoryID], [CategoryName],
[Description] FROM [Categories]"
            UpdateCommand="UPDATE [Categories] Set
[CategoryName]=@CategoryName, [Description]=@Description where
[CategoryID]=@CategoryID"
            >
        </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>


Hope this helps.


Regards,
Walter Wang (waw***@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
Author
29 Nov 2007 10:36 AM
Stefan Dirks
Hi Walter,

thank you very much for your detailed answer. I need some time for testing
and then I'll let you know, if I could solve the problem.

Thanks and regards,

Stefan


""Walter Wang [MSFT]"" <waw***@online.microsoft.com> schrieb im Newsbeitrag
Show quoteHide quote
news:ALLe$1iMIHA.5204@TK2MSFTNGHUB02.phx.gbl...
> Hi Stefan,
>
> Thanks very much for your update.
>
> I have done some research and I think we need to use another approach to
> create dynamic TemplateField at runtime.
>
> Apparently the added field's name is saved in ViewState, but ItemTemplate
> and EditItemTemplate is not, that's why you will see duplicate rows in
> your
> first message. On the other hand, if we only create the new TemplateField
> upon page's first load (Not Me.IsPostBack) and set the ItemTemplate and
> EditItemTemplate every time, we cannot extract the values from the
> EditItemTemplate when updating, due to some internal implementation detail
> of DetailsView.
>
> This turns out not a trivial task to do. We need to create all three types
> of classes here:
>
> * a customized DetailsView class to override CreateFieldSet and add your
> dynamic TemplateField
> * a customized TemplateField class to override the ExtractValuesFromCell
> method to return the data from its template, which is needed by the data
> layer
> * customized ItemTemplate and EditItemTemplate
>
> I'm include the complete code listing here for your reference (I'm using
> the Northwind sample database from SqlServer):
>
> Imports System
> Imports System.Web
> Imports System.Data
> Imports System.Web.UI
> Imports System.Web.UI.WebControls
>
> Public Class MyStringItemTemplate
>    Implements ITemplate
>
>    Private m_column As String
>    Public Sub New(ByVal column As String)
>        m_column = column
>    End Sub
>
>    Public Sub InstantiateIn(ByVal container As System.Web.UI.Control)
> Implements System.Web.UI.ITemplate.InstantiateIn
>        Dim l As New Literal()
>        AddHandler l.DataBinding, AddressOf BindData
>        container.Controls.Add(l)
>    End Sub
>
>    Protected Sub BindData(ByVal sender As Object, ByVal e As EventArgs)
>        Dim l As Literal = sender
>        Dim dv As DetailsView = l.NamingContainer
>        l.Text = DataBinder.Eval(dv.DataItem, m_column)
>    End Sub
> End Class
>
> Public Class MyStringEditItemTemplate
>    Implements ITemplate
>
>    Private m_column As String
>    Public Sub New(ByVal column As String)
>        m_column = column
>    End Sub
>    Public Sub InstantiateIn(ByVal container As System.Web.UI.Control)
> Implements System.Web.UI.ITemplate.InstantiateIn
>        Dim tb As New TextBox()
>        tb.ID = m_column
>        AddHandler tb.DataBinding, AddressOf BindData
>        container.Controls.Add(tb)
>    End Sub
>    Protected Sub BindData(ByVal sender As Object, ByVal e As EventArgs)
>        Dim tb As TextBox = sender
>        Dim dv As DetailsView = tb.NamingContainer
>        tb.Text = DataBinder.Eval(dv.DataItem, m_column)
>    End Sub
> End Class
>
> Public Class MyTemplateField
>    Inherits TemplateField
>
>    Private m_column As String
>    Public Sub New(ByVal column As String)
>        ItemTemplate = New MyStringItemTemplate(column)
>        EditItemTemplate = New MyStringEditItemTemplate(column)
>        m_column = column
>    End Sub
>
>    Public Overrides Sub ExtractValuesFromCell(ByVal dictionary As
> System.Collections.Specialized.IOrderedDictionary, ByVal cell As
> System.Web.UI.WebControls.DataControlFieldCell, ByVal rowState As
> System.Web.UI.WebControls.DataControlRowState, ByVal includeReadOnly As
> Boolean)
>        MyBase.ExtractValuesFromCell(dictionary, cell, rowState,
> includeReadOnly)
>        Dim tb As TextBox = cell.FindControl(m_column)
>        dictionary(m_column) = tb.Text
>    End Sub
> End Class
>
> Public Class MyDetailsView
>    Inherits DetailsView
>
>    Protected Overrides Function CreateFieldSet(ByVal dataItem As Object,
> ByVal useDataSource As Boolean) As System.Collections.ICollection
>        Dim col As New MyTemplateField("Description")
>
>        Dim al As New ArrayList()
>        For Each f As DataControlField In MyBase.CreateFieldSet(dataItem,
> useDataSource)
>            al.Add(f)
>        Next
>        al.Add(col)
>        Return al
>    End Function
> End Class
>
>
>
> <%@ Page Language="vb" AutoEventWireup="false"
> CodeBehind="WebForm2.aspx.vb" Inherits="WebApplication2.WebForm2" %>
>
> <%@ Register assembly="WebApplication2" namespace="WebApplication2"
> tagprefix="cc1" %>
>
> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>
> <html xmlns="http://www.w3.org/1999/xhtml" >
> <head runat="server">
>    <title>Untitled Page</title>
> </head>
> <body>
>    <form id="form1" runat="server">
>    <div>
>
>        <cc1:MyDetailsView ID="MyDetailsView1" runat="server"
> AutoGenerateRows="False"
>         AutoGenerateEditButton="true" DataKeyNames="CategoryID"
>            DataSourceID="SqlDataSource1">
>            <Fields>
>                <asp:BoundField DataField="CategoryID" HeaderText="ID"
> ReadOnly="True"
>                    SortExpression="CategoryID" />
>                <asp:BoundField DataField="CategoryName" HeaderText="Name"
>                    SortExpression="CategoryName" />
>            </Fields>
>        </cc1:MyDetailsView>
>        <asp:SqlDataSource ID="SqlDataSource1" runat="server"
>            ConnectionString="<%$
> ConnectionStrings:NorthwindConnectionString %>"
>            SelectCommand="SELECT [CategoryID], [CategoryName],
> [Description] FROM [Categories]"
>            UpdateCommand="UPDATE [Categories] Set
> [CategoryName]=@CategoryName, [Description]=@Description where
> [CategoryID]=@CategoryID"
>            >
>        </asp:SqlDataSource>
>
>    </div>
>    </form>
> </body>
> </html>
>
>
> Hope this helps.
>
>
> Regards,
> Walter Wang (waw***@online.microsoft.com, remove 'online.')
> Microsoft Online Community Support
>
> ==================================================
> When responding to posts, please "Reply to Group" via your newsreader so
> that others may learn and benefit from your issue.
> ==================================================
>
> This posting is provided "AS IS" with no warranties, and confers no
> rights.
>

Bookmark and Share