.NET中动态控件的问题

时间:2022-07-01 16:03:42

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.

我想创建一些动态控件,并让它们在页面加载时保持它们的viewstate。足够简单,对吗?我所要做的就是使用相同的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.

问题出现在我重新创建了我的预发布事件中的控件之后。重新创建的控件从不绑定到viewstate,它们的值不会跨页面加载而持久。我不明白为什么会这样。我已经在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,其中[情形1]旁边的行没有注释掉,但是[情形2]旁边的行被注释掉了。这里一切正常。当我把这个控件放在页面上并加载页面时,我看到一个链接,上面写着“点击”。当我点击链接时,页面输出文本“[new value]”,在下一行,我们看到熟悉的“click”链接。每次点击“点击”链接,我们都会看到同样的事情。到目前为止还好。

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,其中[情形1]旁边的行被注释掉了,但是[情形2]旁边的行没有被注释掉。这里我们遇到了问题。当我们加载页面时,我们会看到“点击”链接。然而,当我点击链接时,页面输出的是“[]”而不是“[新值]”。单击事件正在正常启动。但是,我分配给控件的value属性的“new 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.

谢谢。

9 个解决方案

#1


19  

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 not backed 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 propeties没有被支持到ViewState(因为页面还没有开始跟踪ViewState)。因此,Init是添加控件和设置(a)属性的好地方,这些属性不会在回发(ID, CssClass…)和动态属性的初始值(然后可以在页面生命周期的其余部分——装载、事件处理程序、预编译器——中通过代码进行修改)之间发生变化。

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。然后,开发人员可以控制为动态添加控件持久化哪些propeties,如下所示:

  • 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.

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

  • 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(从加载事件之前到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: .

在您的案例中,您的预处理器正在设置属性,然后将控件添加到页面的控制树中。为了得到您想要的结果,在将控件添加到控制树后设置动态属性:。

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


3  

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的信息。看看这篇文章:http://msdn.microsoft.com/en-us/library/ms178472.aspx

#3


1  

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。

#4


0  

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.

谢谢你的帮助,但我试过了,没有什么不同。此外,只要每次给控件相同的id, OnLoad对于动态控件和OnInit的工作效果都是一样的。

#5


0  

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 want the ViewState to rebind. My issue is that I was not recreating the previous controls, which is why I think that the pseudo-flag notion above applies.

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

#6


0  

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

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

#7


0  

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在页面及其子对象上工作。在[案例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);

#8


0  

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

#9


0  

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被触发后立即加载控制树来让你的app正常工作。如果重写loadviewstate事件,请调用mybase。loadviewstate然后将您自己的代码重新生成控件,这些控件的值将在页面加载中可用。在我的一个应用程序中,我使用一个viewstate字段来保存可以用来重新创建这些控件的ID或数组信息。

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

#1


19  

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 not backed 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 propeties没有被支持到ViewState(因为页面还没有开始跟踪ViewState)。因此,Init是添加控件和设置(a)属性的好地方,这些属性不会在回发(ID, CssClass…)和动态属性的初始值(然后可以在页面生命周期的其余部分——装载、事件处理程序、预编译器——中通过代码进行修改)之间发生变化。

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。然后,开发人员可以控制为动态添加控件持久化哪些propeties,如下所示:

  • 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.

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

  • 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(从加载事件之前到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: .

在您的案例中,您的预处理器正在设置属性,然后将控件添加到页面的控制树中。为了得到您想要的结果,在将控件添加到控制树后设置动态属性:。

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


3  

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的信息。看看这篇文章:http://msdn.microsoft.com/en-us/library/ms178472.aspx

#3


1  

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。

#4


0  

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.

谢谢你的帮助,但我试过了,没有什么不同。此外,只要每次给控件相同的id, OnLoad对于动态控件和OnInit的工作效果都是一样的。

#5


0  

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 want the ViewState to rebind. My issue is that I was not recreating the previous controls, which is why I think that the pseudo-flag notion above applies.

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

#6


0  

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

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

#7


0  

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在页面及其子对象上工作。在[案例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);

#8


0  

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

#9


0  

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被触发后立即加载控制树来让你的app正常工作。如果重写loadviewstate事件,请调用mybase。loadviewstate然后将您自己的代码重新生成控件,这些控件的值将在页面加载中可用。在我的一个应用程序中,我使用一个viewstate字段来保存可以用来重新创建这些控件的ID或数组信息。

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