【问题标题】:asp.net FindControl Recursivelyasp.net FindControl 递归
【发布时间】:2011-07-13 16:35:30
【问题描述】:

这真的很奇怪 - 我会尽力解释。

我有一个基本的母版页:

<%@ Master Language="VB" CodeFile="MasterPage.master.vb" Inherits="master_MasterPage" %>

<!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></title>
    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
        </asp:ContentPlaceHolder>
        <asp:PlaceHolder ID="PH1" runat="server" />
        <asp:PlaceHolder ID="PH2" runat="server" />
    </div>
    </form>
</body>
</html>

还有一个标准的子页面:

  <%@ Page Title="" Language="VB" MasterPageFile="~/master/MasterPage.master" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="master_Default" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
</asp:Content>

我有以下递归查找控件的扩展方法:

Option Strict On
Option Explicit On

Imports System.Runtime.CompilerServices
Imports System.Web.UI

Public Module ExtensionMethods

    <Extension()> _
    Public Function FindControlRecursively(ByVal parentControl As System.Web.UI.Control, ByVal controlID As String) As System.Web.UI.Control

        If parentControl.ID = controlID Then
            Return parentControl
        End If

        For Each c As System.Web.UI.Control In parentControl.Controls
            Dim child As System.Web.UI.Control = FindControlRecursively(c, controlID)
            If child IsNot Nothing Then
                Return child
            End If
        Next

        Return Nothing

    End Function

    <Extension()> _
    Public Function FindControlIterative(ByVal rootControl As Control, ByVal controlId As String) As Control

        Dim rc As Control = rootControl
        Dim ll As LinkedList(Of Control) = New LinkedList(Of Control)

        Do While (rc IsNot Nothing)
            If rc.ID = controlId Then
                Return rc
            End If
            For Each child As Control In rc.Controls
                If child.ID = controlId Then
                    Return child
                End If
                If child.HasControls() Then
                    ll.AddLast(child)
                End If
            Next
            rc = ll.First.Value
            ll.Remove(rc)
        Loop

        Return Nothing

    End Function

End Module

我有一个带有列表视图的控件:

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="control-1.ascx.vb" Inherits="controls_control_1" %>
<p>
    Control 1</p>
<asp:ListView ID="lv" runat="server">
    <ItemTemplate>
        <div>
            <asp:Literal ID="Name" runat="server" Text='<%#Eval("Name") %>' />
            <asp:LinkButton ID="TestButton" runat="server">Test</asp:LinkButton>
        </div>
    </ItemTemplate>
</asp:ListView>

即数据绑定:

Partial Class controls_control_1
    Inherits System.Web.UI.UserControl

    Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        If Not Page.IsPostBack Then

            Dim l As New List(Of Person)
            Dim j As New Person
            j.Name = "John"
            l.Add(j)

            lv.DataSource = l
            lv.DataBind()

        End If

    End Sub

End Class

Public Class Person
    Public Property Name As String
End Class

我有第二个非常基本的控件:

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="control-2.ascx.vb" Inherits="controls_control_2" %>
<p>Control 2</p>

在我的子页面中,我有以下代码来加载控件:

Option Strict On
Option Explicit On

Partial Class master_Default
    Inherits System.Web.UI.Page

    Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

        Dim controlInstance1 As System.Web.UI.Control = LoadControl("~/controls/control-1.ascx")
        controlInstance1.ID = "control_1"

        Dim zone As System.Web.UI.Control = Me.Master.FindControlRecursively("PH1")

        zone.Controls.Add(controlInstance1)

        Dim controlInstance2 As System.Web.UI.Control = LoadControl("~/controls/control-2.ascx")
        controlInstance2.ID = "control_2"

        Dim zone2 As System.Web.UI.Control = Me.Master.FindControlRecursively("PH2")

        zone2.Controls.Add(controlInstance2)

    End Sub

End Class

这会加载控件,但是如果我单击列表视图中的“测试”按钮,则页面在回发后会丢失列表视图中的数据。

如果我将 FindControlRecursively 调用更改为 FindControlIterative,当我单击测试按钮时,列表视图中的数据会在回发后保留。

有人知道 FindControlRecursively 调用可能会导致列表视图丢失数据吗?仅当将 control-2 添加到页面时才会发生这种情况 - 如果没有,并且使用 FindControlRecursively 加载 control-1,则数据在回发后正确保留。

提前谢谢...这个让我发疯了,我花了一段时间才弄清楚它到底是哪里出了问题。

【问题讨论】:

  • 我为母版页复制了错误的代码。我刚刚更正了。

标签: asp.net dynamic recursion controls findcontrol


【解决方案1】:

为什么不直接暴露返回PH1PH2的属性,因为master有它们的引用,你不需要遍历master的所有子控件:

Public ReadOnly Property Container1 As PlaceHolder
    Get
        Return Me.PH1
    End Get
End Property

Public ReadOnly Property Container2 As PlaceHolder
    Get
        Return Me.PH2
    End Get
End Property

您可以访问它们:

Dim ph1 As PlaceHolder = DirectCast(Me.Master, myMaster).Container1
Dim ph2 As PlaceHolder = DirectCast(Me.Master, myMaster).Container2

另一个问题是这一行:

controlInstance1.ID = "control_2"

您只设置了两次 controlInstance1 的 ID,但这不会导致您的问题。

您的主要问题是您将控件添加到 Page_Init 而不是 Page_Load 中的占位符。因此 UserControls 不能加载他们的 ViewState 并且 ListView 是空的。在 Page_Load 中重新创建它们,它将起作用。

编辑:但我必须承认,我不知道为什么您的迭代扩展胜过递归。占位符上的引用是相同的,它们不应该同时工作,很奇怪。

总结

  • 它适用于我的属性,
  • 将所有内容放在页面的加载事件处理程序中,而不是 init
  • 使用您的迭代扩展(无论出于何种原因)

如果您在通过FindControlRecursively 找到占位符后,最后添加两个用户控件,它也可以工作。

zone.Controls.Add(controlInstance1)
zone2.Controls.Add(controlInstance2)

我对此失去了动力,但我相信你会找到答案hereControls.Add 将其父级的 ViewState 加载到所有子级中,因此它取决于您添加控件的时间,并且其父控件中的控件索引在回发时必须相同才能重新加载 ViewState。

在将 control1 添加到 PH1(搜索 PH2 时)后,您的递归扩展方法会触及 control1 的 ID,而迭代扩展则不会。我认为这会破坏它在 Page_Init 中的 ViewState。

结论改用属性

【讨论】:

  • 不幸的是,我认为我不能使用类型化属性,因为 master 也在运行时设置(这是自制 CMS 的一部分)。 controlInstance1.ID = "control_2" 是一个错字,将在上面修复。我曾经在 Page_Load 中创建了控件,但在其他回发场景中遇到了问题,因为视图状态不可用,实际上移至 Page_Init 已修复。你帖子的后半部分给了我一些想法并且非常有意义,但这种行为对我来说仍然很奇怪。一旦我在我的应用程序中解决了这个问题,我会很快在这里跟进。非常感谢您的建议。
【解决方案2】:

我明白了为什么我会看到我上面描述的行为。我将递归函数更改为以下内容:

<Extension()> _
    Public Function FindControlRecursively(ByVal parentControl As System.Web.UI.Control, ByVal controlId As String) As System.Web.UI.Control

        If String.IsNullOrEmpty(controlId) = True OrElse controlId = String.Empty Then
            Return Nothing
        End If

        If parentControl.ID = controlId Then
            Return parentControl
        End If

        If parentControl.HasControls Then
            For Each c As System.Web.UI.Control In parentControl.Controls
                Dim child As System.Web.UI.Control = FindControlRecursively(c, controlId)
                If child IsNot Nothing Then
                    Return child
                End If
            Next
        End If

        Return Nothing

    End Function

通过添加parentControl.HasControls 检查,我阻止了该函数在列表视图中搜索子控件,这允许列表视图稍后在页面/控件生命周期中加载其视图状态。

另外,我调整了我的迭代函数,使其更高效,并防止它在未返回控件时出错:

<Extension()> _
    Public Function FindControlIteratively(ByVal parentControl As Web.UI.Control, ByVal controlId As String) As Web.UI.Control

        Dim ll As New LinkedList(Of Web.UI.Control)

        While parentControl IsNot Nothing
            If parentControl.ID = controlId Then
                Return parentControl
            End If
            For Each child As Web.UI.Control In parentControl.Controls
                If child.ID = controlId Then
                    Return child
                End If
                If child.HasControls() Then
                    ll.AddLast(child)
                End If
            Next
            If (ll.Count > 0) Then
                parentControl = ll.First.Value
                ll.Remove(parentControl)
            Else
                parentControl = Nothing
            End If
        End While

        Return Nothing

    End Function

另外,为了跟进我之前对问题的描述——如果我从迭代函数中删除了If child.HasControls() Then 检查,我能够使用迭代函数重现递归函数最初奇怪的行为。希望这是有道理的。

最后我坚持使用迭代函数,因为循环应该比递归更便宜,尽管在现实世界的场景中,差异可能不会很明显。

以下链接对我解决这个问题非常有帮助:

http://msdn.microsoft.com/en-us/library/ms972976.aspx#viewstate_topic4

http://www.4guysfromrolla.com/articles/092904-1.aspx

http://scottonwriting.net/sowblog/archive/2004/10/06/162995.aspx

http://scottonwriting.net/sowblog/archive/2004/10/08/162998.aspx

非常感谢 Tim 为我指明了正确的方向。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 2012-10-31
    • 2011-02-04
    相关资源
    最近更新 更多