【问题标题】:Persisting Dynamic Table Across postback跨回发持久化动态表
【发布时间】:2014-12-12 15:23:29
【问题描述】:

我有一个在后面的代码中创建的表。在VB代码中,表基于选择组合值时从查询返回的数据填充表。该代码创建一行,其中包含两个单元格,一个用于标签,一个用于数据中每一行的下拉列表。我无法保留回发后为每一行创建的选定下拉列表值。

最初填充表时,我将数据存储在 ViewState 值中,并根据 PageLoad 中的这些设置重新创建表。问题是每次我更改下拉列表的值,然后通过单击保存设置导致回发发生时,所有设置都被错误地保存,因为它们甚至在调用保存之前就被还原了。

我希望在回发中维护这些值,但在选择新帐户时仍会更新数据库中的值。我尝试了几种方法来做到这一点,但我的代码遇到了两个问题:

当我在标记中的表上有 EnableViewState = "true" 时: 当我从组合框中选择一个项目以选择一个新帐户时,下拉列表中的选定值将保留,此时它们应该清除并使用新的数据库值。

当我在标记中的表上有 EnableViewState = "false" 时: 任何回发都会将下拉列表重置为其数据库值。即使单击立即回发以将值保存到数据库的“保存”,也只会重新保存它们当前的数据库值并忽略选定的值。

后面的代码:

Imports System.Data.SqlClient

Imports Telerik.Web.UI

Public Class AccountSettings2
    Inherits Page

    Private _selectedAccountID As Integer
    Protected _truckPermissions As List(Of PermissionData2)

    Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init

        _truckPermissions = New List(Of PermissionData2)

        If Not IsNothing(Session("SelectedAccountID")) Then
            _selectedAccountID = Session("SelectedAccountID")
        End If

        If Not IsPostBack Then

            Dim dtAccounts As New DataTable("Accounts")
            Dim col1 As DataColumn = New DataColumn()
            col1.DataType = System.Type.GetType("System.Int32")
            col1.ColumnName = "AccountID"
            dtAccounts.Columns.Add(col1)
            Dim col2 As DataColumn = New DataColumn()
            col2.DataType = System.Type.GetType("System.String")
            col2.ColumnName = "Name"
            dtAccounts.Columns.Add(col2)

            Dim row1 As DataRow
            Dim row2 As DataRow
            Dim row3 As DataRow
            Dim row4 As DataRow
            row1 = dtAccounts.NewRow()
            row1("AccountID") = 1
            row1("Name") = "Account 1"
            dtAccounts.Rows.Add(row1)
            row2 = dtAccounts.NewRow()
            row2("AccountID") = 2
            row2("Name") = "Account 2"
            dtAccounts.Rows.Add(row2)
            row3 = dtAccounts.NewRow()
            row3("AccountID") = 3
            row3("Name") = "Account 3"
            dtAccounts.Rows.Add(row3)
            row4 = dtAccounts.NewRow()
            row4("AccountID") = 4
            row4("Name") = "Account 4"
            dtAccounts.Rows.Add(row4)

            rcbAccounts.DataValueField = "AccountID"
            rcbAccounts.DataTextField = "Name"
            rcbAccounts.DataSource = dtAccounts
            rcbAccounts.DataBind()

        End If

    End Sub

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

        If Not IsPostBack Then
            If _selectedAccountID > 0 Then
                rcbAccounts.SelectedValue = _selectedAccountID
                SelectAccount(_selectedAccountID)
            End If
        End If

    End Sub

#Region "UI Updates"

    Private Sub SelectAccount(accountID As Integer)

        _selectedAccountID = accountID
        Session("SelectedAccountID") = accountID
        CreateTruckPermissionsData(accountID)

    End Sub

    Private Sub CreateTruckPermissionsData(accessTypeID As Integer)

        tblTruckPermissions.Rows.Clear()

        _truckPermissions.Add(New PermissionData2(1, "Permission 1", "Permission 1", accessTypeID))
        _truckPermissions.Add(New PermissionData2(2, "Permission 2", "Permission 2", accessTypeID))
        _truckPermissions.Add(New PermissionData2(3, "Permission 3", "Permission 3", accessTypeID))
        _truckPermissions.Add(New PermissionData2(4, "Permission 4", "Permission 4", accessTypeID))
        ViewState("_truckPermissions") = _truckPermissions

        Dim dtAccessTypes As New DataTable("AccessTypes")
        Dim col1 As DataColumn = New DataColumn()
        col1.DataType = System.Type.GetType("System.Int32")
        col1.ColumnName = "AccessTypeID"
        dtAccessTypes.Columns.Add(col1)
        Dim col2 As DataColumn = New DataColumn()
        col2.DataType = System.Type.GetType("System.String")
        col2.ColumnName = "Description"
        dtAccessTypes.Columns.Add(col2)

        Dim row1 As DataRow
        Dim row2 As DataRow
        Dim row3 As DataRow
        Dim row4 As DataRow
        row1 = dtAccessTypes.NewRow()
        row1("AccessTypeID") = 1
        row1("Description") = "Type 1"
        dtAccessTypes.Rows.Add(row1)

        row2 = dtAccessTypes.NewRow()
        row2("AccessTypeID") = 2
        row2("Description") = "Type 2"
        dtAccessTypes.Rows.Add(row2)
        row3 = dtAccessTypes.NewRow()
        row3("AccessTypeID") = 3
        row3("Description") = "Type 3"
        dtAccessTypes.Rows.Add(row3)
        row4 = dtAccessTypes.NewRow()
        row4("AccessTypeID") = 4
        row4("Description") = "Type 4"
        dtAccessTypes.Rows.Add(row4)

        For Each pd As PermissionData2 In _truckPermissions
            Dim tr As New TableRow()
            Dim td As New TableCell()
            Dim td2 As New TableCell()
            Dim l As New Label()
            Dim ddl As New RadDropDownList()

            l.Text = pd.Name
            ddl.ID = "ddlTruckPermission" + pd.ID.ToString()
            ddl.DataTextField = "Description"
            ddl.DataValueField = "AccessTypeID"
            ddl.DataSource = dtAccessTypes
            ddl.DataBind()
            ddl.SelectedValue = pd.HasAccess

            td.Controls.Add(l)
            td2.Controls.Add(ddl)
            tr.Cells.Add(td)
            tr.Cells.Add(td2)

            tblTruckPermissions.Rows.Add(tr)
        Next

    End Sub

    Private Sub RefreshTruckSettings()

        If _selectedAccountID = 0 Then
            Return
        End If

        For Each r As TableRow In tblTruckPermissions.Rows
            For Each c As Control In r.Cells(1).Controls
                If c.ID.Contains("ddlTruckPermission") Then
                    Dim ddl As RadDropDownList = DirectCast(c, RadDropDownList)

                    Dim pd As PermissionData2 = _truckPermissions.Find(Function(x) x.ID = Integer.Parse(ddl.ID.Substring(0 + "ddlTruckPermission".Length, ddl.ID.Length - "ddlTruckPermission".Length)))

                    If Not IsNothing(pd) Then
                        ddl.SelectedValue = pd.HasAccess
                    End If
                End If
            Next
        Next

    End Sub

#End Region

#Region "Events"

    Protected Sub btnSavePermissions_Click(sender As Object, e As EventArgs)

        If _selectedAccountID > 0 Then

            Dim permissionUpdates As List(Of PermissionUpdate) = New List(Of PermissionUpdate)()

            For Each r As TableRow In tblTruckPermissions.Rows
                For Each c As Control In r.Cells(1).Controls
                    If c.ID.Contains("ddlTruckPermission") Then
                        Dim ddl As RadDropDownList = DirectCast(c, RadDropDownList)
                        permissionUpdates.Add(New PermissionUpdate(Integer.Parse(ddl.ID.LastIndexOf("ddlTruckPermission")), False, ddl.SelectedValue))
                    End If
                Next
            Next

            ' Code to save permissions to database

        End If

    End Sub

    Protected Sub rcbAccounts_SelectedIndexChanged(sender As Object, e As RadComboBoxSelectedIndexChangedEventArgs)

        Dim newIndex As Integer

        If (Integer.TryParse(e.Value, newIndex)) Then
            SelectAccount(newIndex)
        End If

    End Sub

#End Region

End Class


<Serializable>
Public Class PermissionData2

    Private _id As Integer
    Private _name As String
    Private _description As String
    Private _hasAccess As Integer

    Public Property ID() As Integer

        Get
            Return _id
        End Get
        Set(value As Integer)
            _id = value
        End Set

    End Property

    Public Property Name() As String

        Get
            Return _name
        End Get
        Set(value As String)
            _name = value
        End Set

    End Property

    Public Property Description() As String

        Get
            Return _description
        End Get
        Set(value As String)
            _description = value
        End Set

    End Property

    Public Property HasAccess() As Integer

        Get
            Return _hasAccess
        End Get
        Set(value As Integer)
            _hasAccess = value
        End Set

    End Property

    Public Sub New()

        _id = 0
        _name = Name
        _description = Description
        _hasAccess = 0

    End Sub

    Public Sub New(id As Integer, name As String, description As String, hasAccess As Integer)

        _id = id
        _name = name
        _description = description
        _hasAccess = hasAccess

    End Sub

End Class

aspx:

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

<!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 id="Head1" runat="server">
    <link rel="stylesheet" type="text/css" href="styles/default.css" />
    <title>Account Settings</title>
</head>
<body>
    <form id="form1" runat="server">

        <asp:ScriptManager runat="server" ID="ScriptManager1"></asp:ScriptManager>

    <asp:UpdatePanel id="UpdatePanel1" runat="server" UpdateMode="Always">
    <ContentTemplate>

        <telerik:RadComboBox ID="rcbAccounts" runat="server" Height="200" Width="200"
            DropDownAutoWidth="Enabled" EmptyMessage="Select an Account" HighlightTemplatedItems="true"
            EnableLoadOnDemand="true" Filter="Contains"
            OnSelectedIndexChanged="rcbAccounts_SelectedIndexChanged" AutoPostBack ="true"
            Label="Accounts: " Skin="Office2010Silver" />

        <asp:Table ID="tblTruckPermissions" runat="server" EnableViewState="true" />

        <asp:Button ID="btnSavePermissions" runat="server" Text="Save" OnClick="btnSavePermissions_Click" />

    </ContentTemplate>
</asp:UpdatePanel>

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

【问题讨论】:

    标签: asp.net vb.net postback


    【解决方案1】:

    好的,现在我已经查看了所有内容,我已经清除了我原来的答案。以下是一些想法:

    第一:

    我会从 Page_Init() 方法中取出所有内容并将其移至 Page_Load() 方法中。 Page_Int() 应该谨慎使用,我看不出在这里使用它的充分理由。 Page_Int() 的问题在于它在大多数对象创建之前在页面的生命周期中过早地被触发,因此您最终会遇到难以理解的奇怪行为。

    我只将它用于与页面相关的逻辑,并且需要在页面加载之前发生,但您应该让它与实际页面对象(按钮、数据网格等)无关。我不会在这里使用它。

    此外,在 Page_Int() 方法中添加“If Not IsPostBack Then”是没有意义的,因为它只会在页面初始化之前被触发一次,然后再也不会触发。

    第二:

    好的。会话(“SelectedAccountID”)。你需要在这里使用会话变量吗?会话变量在整个网站中保持自身,直到用户关闭网站。如果您需要将其作为会话级别变量(在站点的其他位置、其他页面等使用),那就这样吧。

    我们来谈谈_selectedAccountID。在加载此页面之前,是否可能已在网站的其他位置设置了 Session("SelectedAccountID")?这就是我的假设,因为 Session("SelectedAccountID") 是一个 Session 变量。我基于该假设编写下面的代码,并且您希望使用 Session("SelectedAccountID") 的值来设置下拉菜单的初始值。

    从您如何使用 _selectedAccountID 来看,您最初似乎将从 Session("SelectedAccountID") 填充它。并且当下拉列表更改时,您希望将 _selectedAccountID 和 Session("SelectedAccountID") 都重置为所选值。它是否正确?我写代码就好像它是一样的。请参阅下文了解处理该问题的最佳方法。

    接下来,您如何使用 _truckPermissions 不太清楚。但看起来您只需要另一个 ViewState 变量。见下文。

    另外,我喜欢按照调用顺序对页面上的所有内容进行排序。那么,让我们来看看......

    Imports System.Data.SqlClient
    Imports Telerik.Web.UI
    
    Public Class AccountSettings2
    Inherits Page
    
    ' This is your ViewState variable for selectedAccountID.  You will set it first in Page_Load() inside If Not IsPostback.  It can be access from any method in the code behind, and will persist across postbacks.
    Public Property _selectedAccountID() As String
        Get
            Return ViewState("selectedAccountID").ToString()
        End Get
        Set(ByVal value As String)
            ViewState("selectedAccountID") = value
        End Set
    End Property
    
    ' This is your ViewState variable for truckPermissions.  It can be access from any method in the code behind, and will persist across postbacks.
    Public Property _truckPermissions() As List(Of PermissionData2)
        Get
            Return ViewState("truckPermissions")
        End Get
        Set(ByVal value As List(Of PermissionData2))
            ViewState("truckPermissions") = value
        End Set
    End Property
    
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
            ' Anything done inside this If Then will happen ONLY when the page loads for the first time. Never again.
    
            ' Set your initial value for the ViewState variable _selectedAccountID here  
            If Not IsNothing(Session("SelectedAccountID")) Then
                _selectedAccountID = Session("SelectedAccountID")
            Else
                _selectedAccountID = 0 ' Do you need to give this a default value is Session("SelectedAccountID") is empty?  If so, this will work.
            End If
    
            ' Create your DataTable, but to keep things easy to read inside Page_Load(), move the work to another method
            Dim dtAccounts As DataTable = BuildDataTable()
    
            ' Do this here, just to be clean
            rcbAccounts.DataValueField = "AccountID"
            rcbAccounts.DataTextField = "Name"
            rcbAccounts.DataSource = dtAccounts
            rcbAccounts.DataBind()
    
            ' Set the initial value on your dropdown.
            If _selectedAccountID > 0 Then
                rcbAccounts.SelectedValue = _selectedAccountID
                ' SelectAccount(_selectedAccountID) ' I don't think we want to do this here
                CreateTruckPermissionsData(_selectedAccountID) ' But I think we DO want to do this
            End If
    
        End If
    
    End Sub
    
    Private Function BuildDataTable() As DataTable
    
        Dim dtAccounts As New DataTable("Accounts")
    
        dtAccounts.Columns.Add(BuildDataColumn("System.Int32", "AccountID"))
        dtAccounts.Columns.Add(BuildDataColumn("System.String", "Name"))
    
        Dim row As DataRow
        Dim i As Integer
        For i = 0 To 4
            row = dtAccounts.NewRow()
            row("AccountID") = i
            row("Name") = "Account " & i
            dtAccounts.Rows.Add(row)
        Next
    
        Return dtAccounts
    
    End Function
    
    Private Function BuildDataColumn(DataType As String, ColumnName As String) As DataColumn
        Dim newCol As New DataColumn()
        newCol.DataType = System.Type.GetType(DataType)
        newCol.ColumnName = ColumnName
        Return newCol
    End Function
    
    #Region "Events"
    
    Protected Sub btnSavePermissions_Click(sender As Object, e As EventArgs)
    
        If _selectedAccountID > 0 Then
    
            Dim permissionUpdates As List(Of PermissionUpdate) = New List(Of PermissionUpdate)()
    
            For Each r As TableRow In tblTruckPermissions.Rows
                For Each c As Control In r.Cells(1).Controls
                    If c.ID.Contains("ddlTruckPermission") Then
                        Dim ddl As RadDropDownList = DirectCast(c, RadDropDownList)
                        permissionUpdates.Add(New PermissionUpdate(Integer.Parse(ddl.ID.LastIndexOf("ddlTruckPermission")), False, ddl.SelectedValue))
                    End If
                Next
            Next
    
            ' Code to save permissions to database
    
        End If
    
    End Sub
    
    Protected Sub rcbAccounts_SelectedIndexChanged(sender As Object, e As RadComboBoxSelectedIndexChangedEventArgs)
        Dim newIndex As Integer
        If (Integer.TryParse(e.Value, newIndex)) Then
            SelectAccount(newIndex)
        End If
    End Sub
    
    #End Region
    
    
    
    #Region "UI Updates"
    
    Private Sub SelectAccount(accountID As Integer)
        _selectedAccountID = accountID
        Session("SelectedAccountID") = accountID
        CreateTruckPermissionsData(accountID)
    End Sub
    
    Private Sub CreateTruckPermissionsData(accessTypeID As Integer)
        ' this code can be cleaned up too, like I did for building the other table.  I just didn't have time to get to it.
        tblTruckPermissions.Rows.Clear()
    
        _truckPermissions.Add(New PermissionData2(1, "Permission 1", "Permission 1", accessTypeID))
        _truckPermissions.Add(New PermissionData2(2, "Permission 2", "Permission 2", accessTypeID))
        _truckPermissions.Add(New PermissionData2(3, "Permission 3", "Permission 3", accessTypeID))
        _truckPermissions.Add(New PermissionData2(4, "Permission 4", "Permission 4", accessTypeID))
        ViewState("_truckPermissions") = _truckPermissions
    
        Dim dtAccessTypes As New DataTable("AccessTypes")
        Dim col1 As DataColumn = New DataColumn()
        col1.DataType = System.Type.GetType("System.Int32")
        col1.ColumnName = "AccessTypeID"
        dtAccessTypes.Columns.Add(col1)
        Dim col2 As DataColumn = New DataColumn()
        col2.DataType = System.Type.GetType("System.String")
        col2.ColumnName = "Description"
        dtAccessTypes.Columns.Add(col2)
    
        Dim row1 As DataRow
        Dim row2 As DataRow
        Dim row3 As DataRow
        Dim row4 As DataRow
        row1 = dtAccessTypes.NewRow()
        row1("AccessTypeID") = 1
        row1("Description") = "Type 1"
        dtAccessTypes.Rows.Add(row1)
    
        row2 = dtAccessTypes.NewRow()
        row2("AccessTypeID") = 2
        row2("Description") = "Type 2"
        dtAccessTypes.Rows.Add(row2)
        row3 = dtAccessTypes.NewRow()
        row3("AccessTypeID") = 3
        row3("Description") = "Type 3"
        dtAccessTypes.Rows.Add(row3)
        row4 = dtAccessTypes.NewRow()
        row4("AccessTypeID") = 4
        row4("Description") = "Type 4"
        dtAccessTypes.Rows.Add(row4)
    
        For Each pd As PermissionData2 In _truckPermissions
            Dim tr As New TableRow()
            Dim td As New TableCell()
            Dim td2 As New TableCell()
            Dim l As New Label()
            Dim ddl As New RadDropDownList()
    
            l.Text = pd.Name
            ddl.ID = "ddlTruckPermission" + pd.ID.ToString()
            ddl.DataTextField = "Description"
            ddl.DataValueField = "AccessTypeID"
            ddl.DataSource = dtAccessTypes
            ddl.DataBind()
            ddl.SelectedValue = pd.HasAccess
    
            td.Controls.Add(l)
            td2.Controls.Add(ddl)
            tr.Cells.Add(td)
            tr.Cells.Add(td2)
    
            tblTruckPermissions.Rows.Add(tr)
        Next
    
    End Sub
    
    
    #End Region
    

    【讨论】:

    • 我的印象是,在代码隐藏中动态创建对象时,您也必须在每次回发时重新创建它们,这不正确吗?
    • 不!不正确。这就是 ViewState 的全部意义所在。您在页面加载时创建一次。然后他们在回发中坚持在 ViewState 中。相信我。把这个 If-Then 放在适当的位置,看看会发生什么。你会对结果感到满意。每个页面上的每个 Page_Load() 方法都应该有这个 If-Then。每次:-)
    • 我们能以某种方式查看您的所有代码吗?在前面和后面?此外,在您的 Page_Load 中(我可能不清楚这一点),您确实需要区分应该只运行一次的代码(创建并初始填充您的对象)和应该在回发后运行的代码(处理回发信息, ETC)。相应地构造您的 If Not IsPostback。如有疑问,请在 Page_Load 方法上设置一个断点,并在调试中单步执行它以查看到底发生了什么
    • 嘿伙计,我要跑了。我几乎完成了查看您的代码并使用它的工作。想在我不得不去这一天之前完成。我可能会在明天回复你。
    • 好吧,开个玩笑。请参阅上面的答案。我认为我理解你的意图。希望如此。如果没有,也许它至少可以帮助您入门。
    【解决方案2】:

    我找到了一种更好的方法来处理我的场景。我使用中继器控件来处理几乎所有代码,而不是尝试手动执行。我使用http://weblogs.asp.net/infinitiesloop/TRULY-Understanding-Dynamic-Controls-_2800_Part-4_2900_ 来帮助指导我。感谢 Casey Crookston 在这方面给予的大力帮助。

    <asp:Repeater ID="rptTruckPermissions" runat="server" EnableViewState="true" OnItemDataBound="rptTruckPermissions_ItemDataBound">
                                <HeaderTemplate>
                                    <table>
                                </HeaderTemplate>
                                <ItemTemplate>
                                    <tr>
                                        <td><%# DataBinder.Eval(Container.DataItem, "PermissionName") %></td>
                                        <td><telerik:RadDropDownList runat="server" ID="ddlTruckPermissionAccessTypes" /></td>
                                    </tr>
                                </ItemTemplate>
                                <FooterTemplate>
                                    </table>
                                </FooterTemplate>
                            </asp:Repeater>
    
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
            If Not IsPostBack Then
    
                If Not IsNothing(_accountDetails) Then
                    SelectAccount(_accountDetails.AccountID)
                End If
    
            End If
    
        End Sub
    
    Private Sub SelectAccount(accountID As Integer)
    
            _selectedAccountID = accountID
            Session("SelectedAccountID") = accountID
    
            BindTruckPermissions()
    
    
        End Sub
    
        Private Sub BindTruckPermissions()
    
            rptTruckPermissions.DataSource = GetPermissionData(_selectedAccountID, PermissionCategory.Truck)
            rptTruckPermissions.DataBind()
    
        End Sub
    
    Protected Sub rptTruckPermissions_ItemDataBound(sender As Object, e As RepeaterItemEventArgs)
    
            If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
    
                Dim r As DataRowView = CType(e.Item.DataItem, DataRowView)
                Dim ddl As RadDropDownList = CType(e.Item.FindControl("ddlTruckPermissionAccessTypes"), RadDropDownList)
                Select Case r("PermissionTypeID")
    
                    Case PermissionType.LegacyBasic
                        ddl.DataSource = GetLegacyWebAccessTypes(PermissionType.LegacyBasic)
                    Case PermissionType.LegacyPublisher
                        ddl.DataSource = GetLegacyWebAccessTypes(PermissionType.LegacyPublisher)
                End Select
    
                'ddl.ID = "ddlTruckPermission" + pd.ID.ToString()
                ddl.DataTextField = "Description"
                ddl.DataValueField = "AccessTypeID"
                ddl.DataBind()
    
                If IsDBNull(r("AccessTypeID")) Then
                    ddl.SelectedValue = LegacyWebAccessType.NoAccess
                Else
                    ddl.SelectedValue = r("AccessTypeID")
                End If
    
            End If
    
        End Sub
    

    【讨论】:

      猜你喜欢
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      • 2013-01-30
      • 2016-02-29
      • 2020-12-25
      • 2017-11-27
      • 2016-05-27
      • 2011-07-29
      相关资源
      最近更新 更多