Asp.Net 中动态添加的控件

发布于 2024-07-05 15:21:56 字数 424 浏览 5 评论 0原文

我正在尝试了解 asp.net。 我有一个长期的 php 开发人员背景,但我现在面临着学习 asp.net 的任务,并且我在这方面遇到了一些麻烦。 这很可能是因为我试图迫使框架去做一些它不适合的事情——所以我想学习如何“以正确的方式”做到这一点。 :-)

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

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

我在这里走错路了吗?

I'm trying to wrap my head around asp.net. I have a background as a long time php developer, but I'm now facing the task of learning asp.net and I'm having some trouble with it. It might very well be because I'm trying to force the framework into something it is not intended for - so I'd like to learn how to do it "the right way". :-)

My problem is how to add controls to a page programmatically at runtime. As far as I can figure out you need to create the controls at page_init as they otherwise disappears at the next PostBack. But many times I'm facing the problem that I don't know which controls to add in page_init as it is dependent on values from at previous PostBack.

A simple scenario could be a form with a dropdown control added in the designer. The dropdown is set to AutoPostBack. When the PostBack occur I need to render one or more controls denepending on the selected value from the dropdown control and preferably have those controls act as if they had been added by the design (as in "when posted back, behave "properly").

Am I going down the wrong path here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

温柔戏命师 2024-07-12 15:21:57

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

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

You must add your control inside OnInit event and viewstate will be preserved. Don't use if(ispostback), because controls must be added every time, event in postback!
(De)Serialization of viewstate happens after OnInit and before OnLoad, so your viewstate persistence provider will see dynamically added controls if they are added in OnInit.

But in scenario you're describing, probably multiview or simple hide/show (visible property) will be better solution.
It's because in OnInit event, when you must read dropdown and add new controls, viewstate isn't read (deserialized) yet and you don't know what did user choose! (you can do request.form(), but that feels kinda wrong)

﹉夏雨初晴づ 2024-07-12 15:21:57

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

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

真正了解 ViewState 是必须的-读。

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

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

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

After having wrestled with this problem for at while I have come up with these groundrules which seems to work, but YMMV.

  • Use declarative controls whenever possible
  • Use databinding where possible
  • Understand how ViewState works
  • The Visibilty property can go a long way
  • If you must use add controls in an event handler use Aydsman's tip and recreate the controls in an overridden LoadViewState.

TRULY Understanding ViewState is a must-read.

Understanding Dynamic Controls By Example shows some techniques on how to use databinding instead of dynamic controls.

TRULY Understanding Dynamic Controls also clarifies techniques which can be used to avoid dynamic controls.

Hope this helps others with same problems.

暮年 2024-07-12 15:21:57

如果您确实需要使用动态控件,则应执行以下操作:

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

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

阅读 Asp.Net 页面生命周期,特别是视图状态如何工作,你就会变得清楚。

编辑:这是一篇非常好的文章,介绍了视图状态的行为方式以及处理动态控件时应考虑的事项:<链接>

If you truly need to use dynamic controls, the following should work:

  • In OnInit, recreate the exact same control hierarchy that was on the page when the previous request was fulfilled. (If this isn't the initial request, of course)
  • After OnInit, the framework will load the viewstate from the previous request and all your controls should be in a stable state now.
  • In OnLoad, remove the controls that are not required and add the necessary ones. You will also have to somehow save the current control tree at this point, to be used in the first step during the following request. You could use a session variable that dictates how the dynamic control tree was created. I even stored the whole Controls collection in the session once (put aside your pitchforks, it was just for a demo).

Re-adding the "stale" controls that you will not need and will be removed at OnLoad anyway seems a bit quirky, but Asp.Net was not really designed with dynamic control creation in mind. If the exact same control hierarchy is not preserved during viewstate loading, all kinds of hard-to find bugs begin lurking in the page, because states of older controls are loaded into newly added ones.

Read up on Asp.Net page life cycle and especially on how the viewstate works and it will become clear.

Edit: This is a very good article about how viewstate behaves and what you should consider while dealing with dynamic controls: <Link>

仅冇旳回忆 2024-07-12 15:21:57

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

也许您有兴趣了解用于创建此 stackoverflow.com 网站的 ASP.NET MVC? 对于具有 PHP(因此,当涉及到 HTML 和 Javascript 时,全力以赴)背景的您来说,这应该更适合您。

Ah, that's the problem with the leaky abstraction of ASP.NET web forms.

Maybe you'll be interested to look at ASP.NET MVC, which was used for the creation of this stackoverflow.com web site? That should be an easier fit for you, coming from a PHP (thus, pedal-to-the-metal when it comes to HTML and Javascript) background.

紅太極 2024-07-12 15:21:57

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

Well. If you can get out of creating controls dynamicly, then do so - otherwise, what i whould do is to use Page_Load instead of Page_Init, but instead of placing stuff inside the If Not IsPostBack, then set i just directly in the method.

喜爱皱眉﹌ 2024-07-12 15:21:57

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

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

I think the answer here is in the MultiView control, so that for example the dropdown switches between different views in the multi-view.

You can probably even data-bind the current view property of the multiview to the value of the dropdown!

满天都是小星星 2024-07-12 15:21:57

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

The only correct answer was given by Aydsman. LoadViewState is the only place to add dynamic controls where their viewstate values will be restored when recreated and you can access the viewstate in order to determine which controls to add.

仙女山的月亮 2024-07-12 15:21:57

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

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

我还没有测试过这个,但你可以研究一下。

I ran across this in the book "Pro ASP.NET 3.5 in C# 2008" under the section Dynamic Control Creation:

If you need to re-create a control multiple times, you should perform the control creation in the Page.Load event handler. This has the additional benefit of allowing you to use view state with your dynamic control. Even though view state is normally restored before the Page.Load event, if you create a control in the handler for the Page.Load event, ASP.NET will apply any view state information that it has after the Page.Load event handler ends. This process is automatic.

I have not tested this, but you might look into it.

烧了回忆取暖 2024-07-12 15:21:56

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

问题变成先有鸡还是先有蛋。 您需要 ViewState 来创建控件树,并且需要创建控件树来获取 ViewState。 嗯,这几乎是正确的。 有一种方法可以在填充树的其余部分之前获取 ViewState 值。 这是通过覆盖 LoadViewState(...)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 页面序列化到 ViewState 以存储对整个数据网格的更改,允许用户使用 PostBack 进行多次编辑,并最终在单个“保存”操作中提交所有更改。

I agree with the other points made here "If you can get out of creating controls dynamically, then do so..." (by @Jesper Blad Jenson aka) but here is a trick I worked out with dynamically created controls in the past.

The problem becomes chicken and the egg. You need your ViewState to create the control tree and you need your control tree created to get at your ViewState. Well, that's almost correct. There is a way to get at your ViewState values just before the rest of the tree is populated. That is by overriding LoadViewState(...) and SaveViewState(...).

In SaveViewState store the control you wish to create:

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

    return myState
}

When the framework calls your "LoadViewState" override you'll get back the exact object you returned from "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]);
}

I've used this successfully to create ASP.Net pages where a DataSet was serialised to the ViewState to store changes to an entire grid of data allowing the user to make multiple edits with PostBacks and finally commit all their changes in a single "Save" operation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文