4

これは本当に奇妙なものです。説明するために最善を尽くします。

私は基本的なマスターページを持っています:

<%@ 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

非常に基本的な 2 番目のコントロールがあります。

<%@ 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 呼び出しが何をしているのか、誰にも分かりますか? これは、コントロール 2 がページに追加された場合にのみ発生します。追加されていない場合、コントロール 1 が FindControlRecursive を使用して読み込まれると、データはポストバック後に正しく保持されます。

前もって感謝します...これは私を狂わせており、正確にどこが故障しているのかを理解するのにしばらく時間がかかりました.

4

2 に答える 2

4

マスターにはそれらの参照があり、マスターのすべての子コントロールを反復処理する必要がないため、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 の ID のみを 2 回設定していますが、問題は発生しません。

主な問題は、Page_Load ではなく Page_Init のプレースホルダーにコントロールを追加していることです。したがって、UserControls は ViewState を読み込めず、ListView は空です。Page_Load でそれらを再作成すると、機能します。

編集:しかし、反復拡張が再帰に勝る理由がわからないことを認めなければなりません。プレースホルダーの参照は同じですが、両方とも機能しないはずです。

要約

  • それは私のプロパティで動作し、
  • init の代わりにページの load イベント ハンドラに all を入れる
  • あなたの反復拡張機能を使用して(何らかの理由で)

を介してプレースホルダーを見つけた後、最後に両方の UserControls を追加した場合にも機能しますFindControlRecursively

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

私はこれでやる気を失っていますが、ここで答えが見つかると確信しています. Controls.Add親のViewStateをすべての子にロードするため、コントロールをいつ追加するかによって異なります。また、親コントロールのコントロールのインデックスは、ViewStateをリロードするポストバックで同じでなければなりません。

コントロール 1 を PH1 に追加した後 (PH2 を検索している間)、再帰的な拡張メソッドが control1 の ID に触れますが、反復拡張はそうではありません。これにより、Page_Init の ViewState が破損していると思います。

まとめ代わりにプロパティを使用する

于 2011-03-11T21:56:40.507 に答える
3

上記の動作が見られる理由がわかりました。再帰関数を次のように変更しました。

<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に感謝します。

于 2011-03-17T19:08:10.127 に答える