C# .NET 中的动态控件问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/196518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:36:41  来源:igfitidea点击:

Problem with dynamic controls in .NET

c#asp.netdynamiccontrolsviewstate

提问by user27293

Problem with dynamic controls

动态控制问题

Hello all,

大家好,

I'm wanting to create some dynamic controls, and have them persist their viewstate across page loads. Easy enough, right? All I have to do is re-create the controls upon each page load, using the same IDs. HOWEVER, here's the catch - in my PreRender event, I'm wanting to clear the controls collection, and then recreate the dynamic controls with new values. The reasons for this are complicated, and it would probably take me about a page or so to explain why I want to do it. So, in the interests of brevity, let's just assume that I absolutely must do this, and that there's no other way.

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

The problem comes in after I re-create the controls in my PreRender event. The re-created controls never bind to the viewstate, and their values do not persist across page loads. I don't understand why this happens. I'm already re-creating the controls in my OnLoad event. When I do this, the newly created controls bind to the ViewState just fine, provided that I use the same IDs every time. However, when I try to do the same thing in the PreRender event, it fails.

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

In any case, here is my example code :

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

namespace TestFramework.WebControls {

命名空间 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 + "]");
    }
}

}

}

So, let's examine case 1, where the line next to [CASE 1] is not commented out, but the line next to [CASE 2] is commented out. Here, everything works just fine. When I put this control on a page and load the page, I see a link that says "Click". When I click the link, the page outputs the text "[new value]", and on the next line, we see the familiar "Click" link. Every subesquent time I click on the "Click" link, we see the same thing. So far, so good.

因此,让我们检查案例 1,其中 [CASE 1] 旁边的行没有被注释掉,但是 [CASE 2] 旁边的行被注释掉了。在这里,一切正常。当我将此控件放在页面上并加载页面时,我会看到一个显示“单击”的链接。当我点击链接时,页面输出文本“[新值]”,在下一行,我们看到熟悉的“点击”链接。每次我点击“点击”链接时,我们都会看到同样的事情。到现在为止还挺好。

But now let's examine case 2, where the line next to [CASE 1] is commented out, but the line next to [CASE 2] is not commented out. Here we run into problems. When we load the page, we see the "Click" link. However, when I click on the link, the page outputs the text "[]" instead of "[new value]". The click event is firing normally. However, the "new value" text that I assigned to the Value attribute of the control does not get persisted. Once again, this is a bit of a mystery to me. How come, when I recreate the control in OnLoad, everything's fine and dandy, but when I recreate the control in PreRender, the value doesn't get persisted?

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

I feel like there simply has to be a way to do this. When I re-create the control in PreRender, is there some way to bind the newly created control to the ViewState?

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

I've struggled with this for days. Any help that you can give me will be appreciated.

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

Thanks.

谢谢。

采纳答案by Joe

ViewState-backed properties are only persisted to ViewState if the control is currently tracking ViewState. This is by design to keep ViewState as small as possible: it should only contain data that is truly dynamic. The upshot of this is that:

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

ViewState propeties set during the Init event are notbacked to ViewState (because the Page has not yet started tracking ViewState). Thus Init is a good place to add controls and set (a) properties that won't change between postbacks (ID, CssClass...) as well as initial values for dynamic properties (which can then be modified by code in the rest of the page lifecycle - Load, event handlers, PreRender).

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

When dynamically adding controls in Load or PreRender, ViewState is being tracked. The developer can then control which propeties are persisted for dynamically added controls as follows:

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

  • Properties set before the control is added to the page's control tree are not persisted to ViewState. You typically set properties that are not dynamic (ID etc) before adding a control to the control tree.

  • Properties set after the control is added to the page's control tree are persisted to ViewState (ViewState tracking is enabled from before the Load Event to after the PreRender event).

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

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

In your case, your PreRender handler is setting properties before adding the control to the page's control tree. To get the result you want, set dynamic properties after adding the control to the control tree: .

在您的情况下,您的 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
}

回答by Joel Coehoorn

I'm already re-creating the controls in my OnLoad event.

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

That's your problem. OnLoad is too late. Use Init instead.

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

回答by user27293

Thank you for your help, but I tried that and it didn't make a difference. Besides, OnLoad works just as well for dynamic controls as OnInit, as long as you give your controls the same IDs every time.

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

回答by DocMax

I believe that once you have added the dynamic controls to the page in PageLoad, the ViewState is bound to the controls and the "ViewState still needs to be bound" flag (in concept, not an actual flag) is cleared. Then, when you recreate the controls, the existing ViewState is no longer bound.

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

I faced something similar last year, only in my case I did not wantthe ViewState to rebind. My issue is that I was notrecreating the previous controls, which is why I think that the pseudo-flag notion above applies.

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

回答by Mark Cidade

Try calling Page.RegisterRequiresControlState(). You can also use RequiresControlState()to check if it's already been registered.

尝试调用Page.RegisterRequiresControlState()。您也可以使用RequiresControlState()它来检查它是否已经被注册。

回答by Aaron Powell

As others have statement you'll need to ensure that you are creating via the Init method. To learn more about the ASP.NET page life cycle check out this article: http://msdn.microsoft.com/en-us/library/ms178472.aspx

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

回答by Samuel Kim

ViewState works on the Page and its child objects. The new control in [Case 2] has not been added to the Page (or any of its children). In fact, in case of the code above, the object will be out of scope as soon as the OnPreRender method ends and will be garbage collected.

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

If you absolutely have to swap out the control, you will need to remove the old control from its parent using Remove() method and add the new control at the right place using AddAt().

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

If the control was the only child of the parent, the code would be something like the following.

如果控件是父级的唯一子级,则代码将类似于以下内容。

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

回答by Joe

Control added before SaveViewState method called in control life cycle should persist their values. I would concur with Joe's answer. Check this image

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

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

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

回答by Middletone

I figured out yesterday that you can actually make your app work like normal by loading the control tree right after the loadviewstateevent is fired. if you override the loadviewstate event, call mybase.loadviewstate and then put your own code to regenerate the controls right after it, the values for those controls will be available on page load. In one of my apps I use a viewstate field to hold the ID or the array info that can be used to recreate those controls.

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

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