【问题标题】:Problem with dynamic controls in .NET.NET 中的动态控件问题
【发布时间】:2010-09-16 19:50:50
【问题描述】:

动态控件的问题

大家好,

我想要创建一些动态控件,并让它们在页面加载时保持其视图状态。很容易,对吧?我所要做的就是在每次页面加载时使用相同的 ID 重新创建控件。但是,这是一个问题 - 在我的 PreRender 事件中,我想清除控件集合,然后使用新值重新创建动态控件。这样做的原因很复杂,我可能需要大约一页左右的时间来解释我为什么要这样做。所以,为了简洁起见,让我们假设我绝对必须这样做,并且没有其他办法。

问题出现在我在 PreRender 事件中重新创建控件之后。重新创建的控件永远不会绑定到视图状态,并且它们的值不会在页面加载时保持不变。我不明白为什么会这样。我已经在我的 OnLoad 事件中重新创建了控件。当我这样做时,只要我每次都使用相同的 ID,新创建的控件就可以很好地绑定到 ViewState。但是,当我尝试在 PreRender 事件中做同样的事情时,它失败了。

无论如何,这是我的示例代码:

命名空间 TestFramework.WebControls {

public class ValueLinkButton : LinkButton
{
    public string Value
    {
        get
        {
            return (string)ViewState[ID + "vlbValue"];
        }

        set
        {
            ViewState[ID + "vlbValue"] = value;
        }
    }
}

public class TestControl : WebControl
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        Controls.Clear();

        ValueLinkButton tempLink = null;

        tempLink = new ValueLinkButton();
        tempLink.ID = "valueLinkButton";
        tempLink.Click += new EventHandler(Value_Click);

        if (!Page.IsPostBack)
        {
            tempLink.Value = "old value";
        }

        Controls.Add(tempLink);
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);

        ValueLinkButton tempLink = ((ValueLinkButton)FindControl("valueLinkButton"));  //[CASE 1]

        //ValueLinkButton tempLink = new ValueLinkButton();  [CASE 2]

        tempLink.ID = "valueLinkButton";
        tempLink.Value = "new value";
        tempLink.Text = "Click";            

        Controls.Clear();
        Controls.Add(tempLink);
    }

    void Value_Click(object sender, EventArgs e)
    {
        Page.Response.Write("[" + ((ValueLinkButton)sender).Value + "]");
    }
}

}

所以,让我们检查案例 1,其中 [CASE 1] 旁边的行没有被注释掉,但 [CASE 2] 旁边的行被注释掉了。在这里,一切正常。当我将此控件放在页面上并加载页面时,我看到一个显示“单击”的链接。当我点击链接时,页面输出文本“[new value]”,在下一行,我们看到熟悉的“Click”链接。每次我单击“单击”链接时,我们都会看到相同的内容。到目前为止,一切顺利。

但是现在让我们检查案例 2,其中 [CASE 1] 旁边的行被注释掉了,但 [CASE 2] 旁边的行没有被注释掉。在这里,我们遇到了问题。当我们加载页面时,我们会看到“点击”链接。但是,当我单击链接时,页面会输出文本“[]”而不是“[新值]”。点击事件正常触发。但是,我分配给控件的 Value 属性的“新值”文本不会保留。再一次,这对我来说有点神秘。怎么回事,当我在 OnLoad 中重新创建控件时,一切都很好,但是当我在 PreRender 中重新创建控件时,值没有得到持久化?

我觉得必须有一种方法可以做到这一点。当我在 PreRender 中重新创建控件时,有没有办法将新创建的控件绑定到 ViewState?

我已经为此苦苦挣扎了好几天。您能给我的任何帮助将不胜感激。

谢谢。

【问题讨论】:

    标签: c# asp.net dynamic controls viewstate


    【解决方案1】:

    仅当控件当前正在跟踪 ViewState 时,ViewState 支持的属性才会持久保存到 ViewState。这是为了使 ViewState 尽可能小而设计的:它应该只包含真正动态的数据。这样做的结果是:

    在 Init 事件期间设置的 ViewState 属性支持 ViewState(因为页面尚未开始跟踪 ViewState)。因此,Init 是添加控件和设置 (a) 不会在回发(ID、CssClass...)之间更改的属性以及动态属性的初始值(然后可以在其余部分中通过代码修改)的好地方页面生命周期 - 加载、事件处理程序、PreRender)。

    在 Load 或 PreRender 中动态添加控件时,会跟踪 ViewState。然后,开发人员可以控制为动态添加的控件保留哪些属性,如下所示:

    • 在将控件添加到页面的控件树之前设置的属性不会保留到 ViewState。您通常在将控件添加到控件树之前设置非动态属性(ID 等)。

    • 将控件添加到页面的控件树后设置的属性将持久保存到 ViewState(从 Load Event 之前到 PreRender 事件之后启用 ViewState 跟踪)。

    在您的情况下,您的 PreRender 处理程序在将控件添加到页面的控件树之前设置属性。要获得您想要的结果,请在将控件添加到控件树后设置动态属性: .

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        ValueLinkButton tempLink = new ValueLinkButton(); // [CASE 2]        
        tempLink.ID = "valueLinkButton"; // Not persisted to ViewState
        Controls.Clear();
        Controls.Add(tempLink);
        tempLink.Value = "new value";  // Persisted to ViewState
        tempLink.Text = "Click";       // Persisted to ViewState
    }
    

    【讨论】:

      【解决方案2】:

      由于其他人有声明,您需要确保您是通过 Init 方法创建的。要了解有关 ASP.NET 页面生命周期的更多信息,请查看这篇文章:http://msdn.microsoft.com/en-us/library/ms178472.aspx

      【讨论】:

        【解决方案3】:

        我已经在 OnLoad 事件中重新创建控件。

        那是你的问题。 OnLoad 为时已晚。请改用 Init。

        【讨论】:

        • 不,可以在 Load 事件处理程序中创建控件。
        • 是的,在 Load 事件处理程序中创建的控件将与 ViewState 一起使用,前提是它们在每次回发时以相同的方式重新创建。将控件添加到控件树后设置的此类控件的属性将持久保存到 ViewState。
        • 错误。由于 LoadViewState 方法在 OnInit 和 OnLoad 之间运行,因此在 OnInit 完成运行时控件必须存在;否则,视图状态将不会恢复,并且不会跟踪/检测回发数据更改。在 OnLoad 中重新创建的控件将错过加载视图状态和回发数据,并且将只有它们的默认值。如果您改为在 OnInit 中重新创建控件,则会加载视图状态、跟踪回发数据更改并将随后触发事件,并且在 OnLoad 方法运行时控件已准备就绪。
        • 需要明确的是,在 OnLoad 中创建的控件仍然可以持久保存到视图状态,但这没有意义,因为除非在加载视图状态时控件存在,否则该信息将永远不会在回发时加载,这是在 OnLoad 事件之前。
        【解决方案4】:

        感谢您的帮助,但我试过了,但没有任何作用。此外,OnLoad 与 OnInit 一样适用于动态控件,只要您每次为控件提供相同的 ID。

        【讨论】:

          【解决方案5】:

          我相信,一旦您在 PageLoad 中将动态控件添加到页面,ViewState 就会绑定到控件,并且“ViewState 仍需要绑定”标志(在概念上,不是实际标志)被清除。然后,当您重新创建控件时,现有的 ViewState 将不再绑定。

          去年我遇到了类似的情况,只是在我的情况下,我不想要重新绑定 ViewState。我的问题是我没有重新创建以前的控件,这就是我认为上面的伪标志概念适用的原因。

          【讨论】:

          • 实际上有一个私有集合字段Page._registeredControlsRequiringControlState,用于跟踪控件的 ViewState。
          【解决方案6】:

          尝试拨打Page.RegisterRequiresControlState()。也可以使用RequiresControlState()查看是否已经注册。

          【讨论】:

            【解决方案7】:

            ViewState 作用于页面及其子对象。 [案例 2] 中的新控件尚未添加到页面(或其任何子项)。事实上,在上述代码的情况下,一旦 OnPreRender 方法结束,对象就会超出范围并被垃圾回收。

            如果您必须更换控件,则需要使用 Remove() 方法从其父控件中删除旧控件,并使用 AddAt() 在正确的位置添加新控件。

            如果控件是父控件的唯一子控件,则代码如下所示。

            ValueLinkButton tempLink = new ValueLinkButton();
            Control parent = FindControl("valueLinkButton").Parent;
            parent.Remove(FindControl("valueLinkButton"));
            parent.AddAt(0, tempLink);
            

            【讨论】:

              【解决方案8】:

              在控件生命周期中调用 SaveViewState 方法之前添加的控件应保持其值。我同意乔的回答。检查这张图片

              http://emanish.googlepages.com/Asp.Net2.0Lifecycle.PNG

              【讨论】:

              • 如果“坚持”是指“在回发时保持他们的价值观”,那就错了。它们的值将被写入视图状态,但由于这些写入的值是在调用 OnLoad 之前在回发时加载的,因此它们将被丢弃,除非在加载视图状态之前存在控件......在 OnLoad 之前。期望加载已保存的视图状态信息的控件必须在加载视图状态之前存在于 OnLoad 之前。
              【解决方案9】:

              我昨天发现,您实际上可以通过在 loadviewstateevent 触发后立即加载控件树来使您的应用程序正常工作。如果您覆盖 loadviewstate 事件,调用 mybase.loadviewstate,然后在其后放置您自己的代码以重新生成控件,这些控件的值将在页面加载时可用。在我的一个应用程序中,我使用视图状态字段来保存可用于重新创建这些控件的 ID 或数组信息。

              Protected Overrides Sub LoadViewState(ByVal savedState As Object)
                  MyBase.LoadViewState(savedState)
                  If IsPostBack Then
                      CreateMyControls()
                  End If
              End Sub
              

              【讨论】:

                猜你喜欢
                • 2011-03-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-04-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多