asp.net FindControl Рекурсивно - PullRequest
       14

asp.net FindControl Рекурсивно

4 голосов
/ 11 марта 2011

Это действительно странно - я сделаю все возможное, чтобы объяснить.

У меня есть основная мастер-страница:

<%@ 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 добавлен на страницу - если это не так, а элемент control-1 загружается с использованием FindControlRecursively, после обратной передачи данные сохраняются корректно.

Заранее спасибо ... это за рулемЯ сошел с ума, и мне потребовалось некоторое время, чтобы выяснить, где именно это сломалось.

Ответы [ 2 ]

4 голосов
/ 12 марта 2011

Почему бы вам просто не предоставить свойства, которые возвращают PH1 и PH2, потому что мастер имеет ссылку на них, и вам не нужно итерировать все дочерние элементы управления мастера:

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 дважды, но это не вызывает проблем.

Ваша основная проблема заключается в том, что вы добавляете элементы управления к заполнителям в Page_Init вместо Page_Load.Поэтому UserControls не может загрузить свой ViewState, а ListView пуст.Создайте их заново в Page_Load, и это сработает.

Редактировать : Но я должен признать, что не знаю, почему ваше итеративное расширение побеждает рекурсивное.Ссылка на заполнители одинакова, они не должны работать оба, странно.

summary :

  • это работает с моими свойствами,
  • помещает все в обработчик событий загрузки страницы вместо init
  • с вашим итерационным расширением (по любым причинам)

Это также работает, если вы наконец добавили оба UserControls после того, как выЯ нашел заполнители через FindControlRecursively.

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

Я теряю мотивацию на это, но я уверен, что вы найдете ответ здесь .Controls.Add загружает ViewState своего родителя во все дочерние элементы, и поэтому это зависит от того, когда вы добавляете элементы управления, также индекс элементов управления в их родительских элементах управления должен быть одинаковым при обратной передаче для перезагрузки ViewState.

Ваш метод рекурсивного расширения касается идентификатора control1 после того, как вы добавили его в PH1 (при поиске в PH2), итеративное расширение - нет.Я предполагаю, что это повреждает его ViewState в Page_Init.

Заключение Используйте свойства вместо

3 голосов
/ 17 марта 2011

Я понял, почему я вижу поведение, которое я описал выше.Я изменил рекурсивную функцию следующим образом:

<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

Большое спасибо Тиму за то, что он указал мне правильное направление.

...