在ASP.Net中动态更改用户控件

11

我正在尝试创建一个网页,根据下拉列表的选择值来显示相应的用户控件。

基本上页面布局如下:

下拉选择
< 根据下拉选择创建的用户控件 >

我已经做到了一半...当选择更改时,控件也在改变。在OnInit()中,我动态创建了最后选定的控件(其值保存在会话状态中,因为在OnInit()中ViewState不可用)。

当下拉选择更改发生时,我删除旧的用户控件,并添加一个新的。问题是:由于新控件是从更改事件添加的,我无法在第一次回发中保存用户更改。在第一次回发之后,所选的控件将从OnInit()中创建,然后一直保留状态,直到下次选择更改。

这是SelectionChanged方法:

protected void SelectionChanged(object sender, EventArgs e)
{
    SelectedValue = int.Parse(DropDownList.SelectedValue);  //Store in Session
    Control userControl = GetSpecificUserControl(SelectedValue);
    PlaceHolder1.Controls.Clear();   // Remove old user control
    PlaceHolder1.Controls.Add(userControl);
}

在SelectionChanged事件发生后,用户对新控件所做的任何更改都不会在接下来的回发中保存。但是,随后的回发将会保存这些更改。此时,该控件在OnInit()中被创建。

是否有一种方法可以在控件更改时强制正确的回发和ViewState?是否可以在控件更改后强制页面重新初始化?

4个回答

11
您需要做的是将DropDownList的最后一个已知值保留在Session中。 然后:
OnInit: 创建由会话中保存的值所指示的任何控件
SelectionChanged事件: - 删除OnInit期间创建的任何控件 - 基于新的DropDownList选择创建并添加新控件 - 在会话中保存新的DropDownList选择
这样,在更改后的下一次postback时,您将重新创建ViewState期望找到的控件,因此它的状态将被恢复。
动态控件可能非常棘手。通常更容易创建您可能需要的所有控件并将它们的Visible属性设置为false。 这样它们根本不会呈现给浏览器。 然后,只有在需要它们时,才将可见性设置为true。

不幸的是,这已经是我所做的了。(SelectedValue是一个属性,它会导致值被存储在会话中。)问题是新创建的控件在下一次回发时没有保存更改。 - grimus
2
这是因为ViewState尚未保存新控件ID的状态,而ViewState不希望看到它。您需要将控件注册到ViewState - 我建议使用我的解决方案#1,或者像Joel建议的那样添加所有控件并使它们不可见。请记住,设置Visible=false意味着没有任何内容被渲染 - 因此,如果您拥有的控件数量可管理,则通常这是一个好的解决方案。 - womp
+1 防止进一步掉发。我所要做的就是将其中一个持久化值切换为存储在 Session 中,而不是 ViewState,仿佛魔术般它开始工作了! - Sir Crispalot
这只是一个归档评论,用于补充@grimus的经验。如果您不小心始终明确设置相同的控件ID以重新创建控件,则可能会发生他描述的确切问题(当按照Joel上述的oninit / event构建时)。 - Base

6
这是与ASP.Net Webforms相关的经典问题,让人抓狂。你有几个选择:
1)这是一种有些不正规的方法,因为它在某种程度上超出了预期的页面生命周期,但根据我的经验,这是处理问题最直接的方法。当页面从下拉列表选择事件提交后,在Init()期间轮询Request["MyDropDownID"]以获取下拉列表控件的选定值 - 不要等到OnMyDropDownChanged()事件来设置您的页面。
2)为用户控件实现自己的ViewState处理。这需要深入了解ViewState文档并覆盖多个方法。
3)Joel的解决方案。他比我更快地发布了帖子:p 其他选择涉及使用JavaScript等方式进行值的提交,但这些会变得非常混乱。

你如何确定 Request["MyDropDownID"] 的字符串? - grimus
DropDown1.UniqueID 真是个菜鸟! - grimus
是的!这个方法可行!谢谢谢谢谢谢!我觉得我应该为其他人指出这一点... ASP.Net非常挑剔。在遵循womp的请求方法后,如果我执行以下操作:OnInit() { PlaceHolder.Controls.Add(oldcontrol); PlaceHolder.Controls.Clear(); PlaceHolder.Controls.Add(newControl); }...它不起作用。 如果我这样做:PlaceHolder.Controls.Add(newControl);...那么它就可以工作了!万岁! - grimus
对于 Request[...] 建议点个赞。在此之前,我一直为此苦恼不已。 - Kevin
+1 对于我也提出的 Request[...] 建议。非常感谢。 - Jonathan Williams
拯救了我的生命,我的头发都危险了 :-)! - Petr Abdulin

1
如果选项列表不太大,您可以静态地呈现所有用户控件,并使用JavaScript / jQuery根据下拉列表的值显示/隐藏适当的控件(onchange js事件)。在保存时,您可以使用下拉列表值从用户控件中提取适当的值。
这样可以避免处理动态控件的痛苦,在从下拉列表进行选择时提供更响应的UI(无需回发),等等...

-4
不要在SelectedIndexChanged处理程序中添加控件,而是在Page_Load期间添加控件。每次页面加载时,您只需要测试下拉列表的值,并为该值加载正确的控件。

1
不要在页面加载期间添加动态控件。那已经太晚了。 - Joel Coehoorn
这并不能解决问题。在ViewState加载完成后,Page_Load()仍然会发生。 - womp
这是ASP.Net 1.1吗? - womp
顺便说一下,这将加载已发布的值,但不会加载状态。 - womp
最初是这样,现在已经更新到3.5版本了。你可能对状态有所了解,但我只关心数值...(无论如何都要使用动态数据重写整个程序,似乎可以更好地处理整个情况) - CodeRedick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接