FindControlRecursively

FindControlRecursively

这真是一个奇怪的问题-我会尽力解释。

我有一个基本的母版页:

<%@ 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调用可能会导致listview丢失其数据吗?仅在将control-2添加到页面上时才会发生这种情况-如果未添加,并且使用FindControlRecursively加载了control-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中。因此,用户控件无法加载其ViewState,并且ListView为空。在Page_Load中重新创建它们,它将起作用。

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

摘要:


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


在通过FindControlRecursively找到占位符后,如果您最后同时添加了两个UserControl,它也将起作用。

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


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

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

结论改用属性

09-25 16:16