asp.net FindControl Recursively

前端 未结 2 844
礼貌的吻别
礼貌的吻别 2020-12-21 05:40

This is a really weird one - I will do my best to explain.

I have a basic master page:

<%@ Master Language=\"VB\" CodeFile=\"MasterPage.master.vb\         


        
相关标签:
2条回答
  • 2020-12-21 06:12

    Why don't you simply expose properties that return PH1 and PH2, because the master has the reference of them and you don't need to iterate all child controls of 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
    

    You can access them:

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

    Another problem is this line:

    controlInstance1.ID = "control_2"
    

    You are setting only controlInstance1's ID twice, but that doesn't cause your issue.

    Your main problem is that you are adding the controls to the placeholders in Page_Init instead of Page_Load. Therefore the UserControls can't load their ViewState and the ListView is empty. Recreate them in Page_Load and it will work.

    Edit: But i must admit that i don't know why your iterate extension wins over the recursive. The reference on the placeholders are the same, they shouldn't work both, weird.

    summary:

    • it works with my properties,
    • putting all in page's load event handler instead init
    • with your iterate-extension(for whatever reasons)

    It also works if you add both UserControls at last, after you've found the placeholders via FindControlRecursively.

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

    I'm losing motivation on this, but i'm sure you'll find the answer here. Controls.Add loads it's parent's ViewState into all childs and therefore it depends on when you add the controls, also the index of the controls in their parent controls must be the same on postback to reload the ViewState.

    Your recursive extension method touches control1's ID after you've added it to PH1(while searching PH2), the iterative extension does not. I assume that this corrupts it's ViewState in Page_Init.

    Conclusion Use properties instead

    0 讨论(0)
  • 2020-12-21 06:25

    I figured out why I was seeing the behavior I described above. I changed the recursive function to the following:

    <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
    

    By adding the parentControl.HasControls check, I am preventing the function from searching the listview for child controls, and this allows the listview to load its viewstate later on in the page/control lifecycle.

    Also, I tweaked my iterative function to make it more efficient and prevent it from bugging out if a control was not returned:

    <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
    

    Also, to follow up on my earlier description of the problem - I was able to reproduce the recursive function's intially weird behavior using the iterative function if I removed the If child.HasControls() Then check from the iterative function. Hope that makes sense.

    In the end I am sticking with the iterative function because looping should be less expensive than recursion, though in real world scenarios the difference probably will not be noticeable.

    The following links were extremely helpful to me in working this out:

    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

    Extra thanks to Tim for pointing me in the right direction.

    0 讨论(0)
提交回复
热议问题