动态控制问题
大家好,
我想创建一些动态控件,并让它们在页面加载中保持其viewstate。够容易吧?我所要做的就是在每次加载页面时使用相同的ID重新创建控件。但是,这是捕获 - 在我的PreRender事件中,我想要清除控件集合,然后使用新值重新创建动态控件。造成这种情况的原因很复杂,我可能需要大约一页来解释我为什么要这样做。因此,为了简洁起见,我们假设我绝对必须这样做,并且别无他法。
在我的PreRender事件中重新创建控件后,问题出现了。重新创建的控件永远不会绑定到视图状态,并且它们的值不会跨页面加载持续存在。我不明白为什么会这样。我已经在我的OnLoad事件中重新创建了控件。当我这样做时,新创建的控件绑定到ViewState就好了,前提是我每次都使用相同的ID。但是,当我尝试在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]旁边的行被注释掉了。一切都很好。当我将此控件放在页面上并加载页面时,我看到一个“Click”链接。当我点击链接时,页面输出文本“[新值]”,在下一行,我们看到熟悉的“点击”链接。我点击“点击”链接的每个次要时间,我们都看到同样的事情。到目前为止,非常好。
但现在让我们来看一下案例2,其中[CASE 1]旁边的行被注释掉了,但[CASE 2]旁边的行没有被注释掉。在这里我们遇到了问题。加载页面时,我们会看到“点击”链接。但是,当我点击链接时,页面输出文本“[]”而不是“[新值]”。 click事件正常触发。但是,我分配给控件的Value属性的“新值”文本不会保持不变。再一次,这对我来说有点神秘。为什么当我在OnLoad中重新创建控件时,一切都很好,但是当我在PreRender中重新创建控件时,值不会持续存在?
我觉得必须有一种方法可以做到这一点。当我在PreRender中重新创建控件时,有没有办法将新创建的控件绑定到ViewState?
我已经挣扎了好几天了。我们将非常感谢您提供的任何帮助。
感谢。
答案 0 :(得分:19)
如果控件当前正在跟踪ViewState,则ViewState支持的属性仅保留到ViewState。这是为了使ViewState尽可能小:它应该只包含真正动态的数据。结果就是:
在Init事件期间设置的ViewState propeties 不支持ViewState(因为页面尚未开始跟踪ViewState)。因此,Init是添加控件和设置(a)不会在回发之间发生变化的属性(ID,CssClass ...)以及动态属性的初始值(可以通过其余部分中的代码修改)的好地方页面生命周期 - 加载,事件处理程序,PreRender)。
在Load或PreRender中动态添加控件时,正在跟踪ViewState。然后,开发人员可以控制动态添加控件的持久性,如下所示:
将控件添加到页面控件树之前设置的属性不会持久保存到ViewState。在将控件添加到控件树之前,通常会设置非动态属性(ID等)。
将控件添加到页面控件树后设置的属性将持久保存到ViewState(从加载事件之前到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
}
答案 1 :(得分:3)
正如其他人声明的那样,您需要确保通过Init方法创建。要了解有关ASP.NET页面生命周期的更多信息,请查看以下文章:http://msdn.microsoft.com/en-us/library/ms178472.aspx
答案 2 :(得分:1)
我已经在OnLoad事件中重新创建了控件。
那是你的问题。 OnLoad为时已晚。改为使用Init。
答案 3 :(得分:0)
感谢您的帮助,但我尝试了这一点并没有什么不同。此外,只要你每次为控件提供相同的ID,OnLoad的工作方式与OnInit一样。
答案 4 :(得分:0)
我相信一旦你将动态控件添加到PageLoad中的页面,ViewState就会绑定到控件上,并且“ViewState仍然需要被绑定”标志(在概念上,而不是实际标志)被清除。然后,当您重新创建控件时,现有的ViewState不再绑定。
去年我遇到了类似的事情,只是在我的情况下,我没有希望 ViewState重新绑定。我的问题是我不重新创建以前的控件,这就是为什么我认为上面的伪标志概念适用。
答案 5 :(得分:0)
尝试拨打Page.RegisterRequiresControlState()
。您也可以使用RequiresControlState()
检查它是否已经注册。
答案 6 :(得分:0)
ViewState适用于Page及其子对象。 [案例2]中的新控件尚未添加到页面(或其任何子项)中。实际上,在上面的代码的情况下,一旦OnPreRender方法结束并且将被垃圾收集,该对象将超出范围。
如果您必须更换控件,则需要使用Remove()方法从其父控件中删除旧控件,并使用AddAt()在正确的位置添加新控件。
如果控件是父控件的唯一子控件,则代码如下所示。
ValueLinkButton tempLink = new ValueLinkButton();
Control parent = FindControl("valueLinkButton").Parent;
parent.Remove(FindControl("valueLinkButton"));
parent.AddAt(0, tempLink);
答案 7 :(得分:0)
在控件生命周期中调用的SaveViewState方法之前添加的控件应该保持其值。我会同意乔的回答。检查此图片
答案 8 :(得分:0)
我昨天想通过在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