11

我正试图围绕asp.net。我有一个长期 php 开发人员的背景,但我现在面临学习 asp.net 的任务,我遇到了一些麻烦。这很可能是因为我试图将框架强加于它不打算用于的东西 - 所以我想学习如何“以正确的方式”去做。:-)

我的问题是如何在运行时以编程方式将控件添加到页面。据我所知,您需要在 page_init 创建控件,否则它们会在下一次 PostBack 时消失。但是很多时候我面临的问题是我不知道在 page_init 中添加哪些控件,因为它依赖于以前 PostBack 中的值。

一个简单的场景可能是在设计器中添加了一个下拉控件的表单。下拉菜单设置为 AutoPostBack。当 PostBack 发生时,我需要根据下拉控件中的选定值呈现一个或多个控件,并且最好让这些控件的行为就像它们是由设计添加的一样(如“在回发时,行为“正确”)。

我在这里走错路了吗?

4

9 回答 9

9

我同意这里提出的其他观点“如果您可以摆脱动态创建控件,那么就这样做......”(@ Jesper Blad Jenson aka)但这是我过去使用动态创建的控件制定的一个技巧。

问题变成了先有鸡还是先有蛋。您需要您的 ViewState 来创建控件树,并且您需要创建您的控件树来获取您的 ViewState。嗯,这几乎是正确的。有一种方法可以在填充树的其余部分之前获取您的 ViewState 值。那是通过覆盖LoadViewState(...)and SaveViewState(...)

在 SaveViewState 中存储您要创建的控件:

protected override object SaveViewState()
{
    object[] myState = new object[2];
    myState[0] = base.SaveViewState();
    myState[1] = controlPickerDropDown.SelectedValue;

    return myState
}

当框架调用您的“LoadViewState”覆盖时,您将取回您从“SaveViewState”返回的确切对象:

protected override void LoadViewState(object savedState) 
{
    object[] myState = (object[])savedState;

    // Here is the trick, use the value you saved here to create your control tree.
    CreateControlBasedOnDropDownValue(myState[1]);

    // Call the base method to ensure everything works correctly.
    base.LoadViewState(myState[0]);
}

我已经成功地使用它来创建 ASP.Net 页面,其中 DataSet 被序列化到 ViewState 以存储对整个数据网格的更改,从而允许用户使用 PostBacks 进行多次编辑并最终在单个“保存”中提交所有更改手术。

于 2008-09-22T06:55:57.387 回答
3

您必须在 OnInit 事件中添加您的控件,并且视图状态将被保留。不要使用if(ispostback),因为每次都必须添加控件,事件在回发!
(De) 视图状态的序列化发生在 OnInit 之后和 OnLoad 之前,因此如果在 OnInit 中添加它们,您的视图状态持久性提供程序将看到动态添加的控件。

但是在您描述的场景中,多视图或简单的隐藏/显示(可见属性)可能是更好的解决方案。
这是因为在 OnInit 事件中,当您必须读取下拉菜单并添加新控件时,视图状态尚未读取(反序列化)并且您不知道用户选择了什么!(你可以做 request.form(),但感觉有点不对)

于 2008-09-22T06:23:52.067 回答
2

在与这个问题搏斗了一段时间之后,我想出了这些似乎可行的基本规则,但是 YMMV。

  • 尽可能使用声明性控件
  • 尽可能使用数据绑定
  • 了解 ViewState 的工作原理
  • Visibilty 属性可以走很长的路
  • 如果您必须在事件处理程序中使用添加控件,请使用 Aydsman 的提示并在重写的 LoadViewState 中重新创建控件。

真正理解 ViewState是必读的。

通过示例了解动态控件展示了一些关于如何使用数据绑定而不是动态控件的技术。

《真正理解动态控制》还阐明了可用于避免动态控制的技术。

希望这可以帮助其他有同样问题的人。

于 2008-09-30T08:07:14.440 回答
1

如果您确实需要使用动态控件,则应使用以下方法:

  • 在 OnInit 中,重新创建与满足前一个请求时页面上完全相同的控制层次结构。(如果这不是最初的请求,当然)
  • 在 OnInit 之后,框架将从先前的请求中加载视图状态,并且您的所有控件现在都应该处于稳定状态。
  • 在 OnLoad 中,删除不需要的控件并添加必要的控件。此时,您还必须以某种方式保存当前的控制树,以便在以下请求的第一步中使用。您可以使用一个会话变量来指示动态控制树的创建方式。我什至将整个 Controls 集合存储在会话中一次(放下你的干草叉,这只是为了演示)。

重新添加您将不需要并且无论如何都会在 OnLoad 中删除的“陈旧”控件似乎有点古怪,但 Asp.Net 的设计并没有真正考虑到动态控件的创建。如果在视图状态加载期间没有保留完全相同的控件层次结构,则各种难以发现的错误开始潜伏在页面中,因为旧控件的状态被加载到新添加的控件中。

阅读 Asp.Net 页面生命周期,尤其是视图状态的工作原理,它将变得清晰。

编辑:这是一篇关于视图状态如何表现以及在处理动态控件时应该考虑什么的非常好的文章:http: //geekswithblogs.net/FrostRed/archive/2007/02/17/106547.aspx

于 2008-09-22T06:46:46.753 回答
0

好。如果您可以摆脱动态创建控件,那么就这样做 - 否则,我应该做的是使用 Page_Load 而不是 Page_Init,而不是在 If Not IsPostBack 中放置东西,然后直接在方法中设置 i。

于 2008-09-22T06:12:55.447 回答
0

我认为这里的答案在MultiView控件中,例如,下拉菜单在多视图中的不同视图之间切换。

您甚至可以将多视图的当前视图属性数据绑定到下拉列表的值!

于 2008-09-22T06:13:00.987 回答
0

啊,这就是 ASP.NET Web 表单的泄漏抽象问题。

也许您会对查看用于创建此 stackoverflow.com 网站的 ASP.NET MVC 感兴趣?这应该更适合您,来自 PHP(因此,当涉及到 HTML 和 Javascript 时,踏板到金属)背景。

于 2008-09-22T06:13:48.457 回答
0

Aydsman 给出了唯一正确的答案。LoadViewState 是添加动态控件的唯一位置,其中重新创建时将恢复其视图状态值,并且您可以访问视图状态以确定要添加的控件。

于 2008-09-23T15:08:10.317 回答
0

我在“C# 2008 中的 Pro ASP.NET 3.5”一书中的动态控件创建部分下遇到了这个问题:

如果需要多次重新创建控件,则应在 Page.Load 事件处理程序中执行控件创建。这还有一个额外的好处,就是允许您在动态控件中使用视图状态。即使视图状态通常在 Page.Load 事件之前恢复,如果您在 Page.Load 事件的处理程序中创建一个控件,ASP.NET 将应用它在 Page.Load 事件处理程序结束后拥有的任何视图状态信息。这个过程是自动的。

我没有对此进行测试,但您可以研究一下。

于 2008-09-23T22:43:11.300 回答