【问题标题】:ASP.NET custom control: when is LoadPostData() called?ASP.NET 自定义控件:何时调用 LoadPostData()?
【发布时间】:2011-03-13 18:38:48
【问题描述】:

我开发了一个扩展 ListBox 的自定义控件。这个想法是控件“记住”在客户端发生的对其元素的修改,例如作为 AJAX 请求的结果。

它的工作方式是控件还呈现隐藏输入,AJAX 请求的结果存储在隐藏输入中。这被回发,控件的 LoadPostData() 方法查找隐藏的输入,如果隐藏的输入有数据,则从中创建 ListItem 集合。

只要用户从列表框中进行选择,它就可以完美运行。如果没有,则不会调用 LoadPostData() 方法,因此不会创建新的 ListItem 集合。 (我已经使用调试器建立了这一点。)

我假设仅当 POST 数据集合包含与控件的 UniqueID 对应的数据(即 HTML 中的“名称”属性)时才调用 LoadPostData 方法。如果用户尚未从列表框中进行选择,则列表框的 UniqueID 的发布数据中不包含任何内容,并且不会调用 LoadPostData()。对吗?

谁能建议我如何确保在每次回发时调用我的自定义 ListBox 的 LoadPostData() 方法,而不管用户是否做出选择?

在此先感谢 - 我真的很喜欢这个。

大卫

【问题讨论】:

    标签: asp.net postback custom-controls viewstate postdata


    【解决方案1】:

    我在这方面有点晚了,但为了将来参考,这里是我如何完成类似的事情......

    我的控件是一个使用模板作为节点的树。我正在处理的问题是如何捕获客户端对节点展开/折叠状态的更改。最终起作用的是:

    在 CreateChildControls 中,将隐藏字段添加到我的根控件的控件集合中。

    protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
    {
        ...
        _cdExpanded = new HiddenField();
        _cdExpanded.ID = "cdExpanded";
        this.Controls.Add(_cdExpanded);
        ...
    }
    

    在 OnInit 调用中

    protected override void OnInit(EventArgs e)
    {
        ...
        Page.RegisterRequiresPostBack(this);
        ...
    }
    

    在 LoadPostData 中查找帖子集合中与隐藏字段的 UniqueID(不是 ClientID)匹配的值:

    public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
    {
        ...
        string cdExpanded = postCollection[_cdExpanded.UniqueID];
        ...
    }
    

    在单个节点的类中,我有代码通过调用 JavaScript 函数来填充切换按钮的 onclick 事件,该函数将基本控件的 ID 和单个节点作为参数。

        string ToggleScript
        {
            get
            {
                return "ToggleNode('" + this.ClientID + "', '" + _TreeRoot.ClientID + "');";
            }
        }
        protected override void Render(HtmlTextWriter writer)
        {
            ...
            if (this.HasChildren)
            {
                writer.AddAttribute("onclick", ToggleScript);
            }
            ...
        }
    

    这使得通过 getElementById 找到隐藏字段变得相当容易:

    function ToggleNode(nodeID, treeID) {
    var cdExpanded = document.getElementById(treeID + "_cdExpanded");
    ...
    }
    

    JavaScript 然后根据发生事件的需要修改隐藏字段的值。当我们回到服务器时,我能够解析出该字段的内容并根据需要修改控件状态,然后再重新渲染。 (注意:我实际上使用了 3 个隐藏字段来跟踪不同的事件,但概念是一样的)

    希望这对未来的其他人有所帮助......

    【讨论】:

    • 感谢您的贡献。
    【解决方案2】:

    仅在选择项目时才工作听起来很奇怪。检查是否正在调用 LoadPostData 的一种快速方法是启用跟踪并将以下内容放入 IPostBackDataHandler.LoadPostData(...)。

    Page.Trace.Write("My control", "LoadPostData");
    

    如果是这种情况,您应该确保拥有以下内容:

    Page.RegisterRequiresPostBack(this) in OnInit
    

    这是一个完整的示例控件。

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.ComponentModel.Design;
    using System.ComponentModel;
    using System.Web.UI.Design;
    
    namespace Controls
    {
        public sealed class ExtendedListBoxDesigner : ControlDesigner
        {
    
            public override string GetDesignTimeHtml()
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("<div>My designer</div>");
                return sb.ToString();
            }
    
        }
    
        [DesignerAttribute(typeof(ExtendedListBoxDesigner), typeof(IDesigner))]
        public class ExtendedListBox : ListBox, INamingContainer, IPostBackDataHandler 
        {
            bool IPostBackDataHandler.LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
            {
                Page.Trace.Write("ExtendedListBox", "LoadPostData");
                return true;
            }
    
    
            protected override void OnInit(EventArgs e)
            {
                Page.RegisterRequiresPostBack(this);
                base.OnInit(e);
            }
    
            protected override void RenderContents(HtmlTextWriter writer)
            {
                base.RenderContents(writer);
                writer.Write(string.Format("<input type=\"hidden\" name=\"{0}_dummy\" value=\"alwaysPostBack\">", this.ID));
            }
    
        }
    }
    

    页面看起来像这样。

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ControlSamlpe._Default" Trace="true" %>
    <%@ Register Assembly="Controls" Namespace="Controls" TagPrefix="sample" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
        <sample:ExtendedListBox runat="server" ID="extListBox"></sample:ExtendedListBox>
        <asp:Button runat="server" ID="go" />
        </div>
        </form>
    </body>
    </html>
    

    当您单击 go 时,您应该会在跟踪中看到“LoadPostData”。

    【讨论】:

    • 只有当一个项目被选中时它才起作用一点也不奇怪。仅当 postdata 包含与控件的 UniqueID 同名的项目时才调用 LoadPostData() 方法 - 如果用户没有选择项目,则不会调用。
    • 嗯,我的第一反应是当我开始写它的时候感觉很奇怪! :) 您是否尝试在 RenderContents 中添加虚拟字段?这样 LoadPostData 总是会被调用。
    • 感谢您关于创建与控件的 UniqueID 具有相同名称属性的“假”元素的建议。我有点明白你的意思。在我看来,它不像我提供的解决方案那么干净。你怎么看?
    • 你是对的,你仍然需要 Page.RegisterRequiresBostBack(this)。我已经用完整的示例修改了我的答案。
    • 感谢您关注 Page.RegisterRequiresPostback。这是确保始终调用 LoadPostData() 的方法,无论发布数据中是否存在与控件的 UniqueID 同名的项目。但是,我发现使用此方法有时不会保留列表框的选定值。我的解决方案(使用在 OnLoad() 期间调用的方法)是唯一似乎始终有效的解决方案,因此我将其标记为正确。
    【解决方案3】:

    我已经确定除非发布数据包含与控件的 UniqueID 同名的项目,否则不会调用 LoadPostData() 方法。 [编辑:在 Init() 期间调用 Page.RegisterRequiresPostback 克服了这个问题。] 我知道为什么,但它非常有限。

    我通过在 LoadPostData() 方法期间根本不处理它来克服这个问题。相反,我用我在 OnLoad() 中调用的方法来处理它。

    在使用这种方法时需要牢记两点:

    1) 您不再有权访问作为参数传递给 LoadPostData() 方法的 postCollection NameValueCollection 对象。这意味着您必须从 Request.Form 集合中提取帖子数据,这会稍微困难一些。 2) 由于 OnLoad() 发生在 ViewState 处理代码之后,因此您需要在创建 ListItems 后手动设置 SelectedValue。如果不这样做,如果列表框是通过 AJAX 填充的并且用户进行了选择,那么选择将会丢失。

    我希望这对将来的某人有所帮助。

    【讨论】:

      猜你喜欢
      • 2010-09-23
      • 2011-06-15
      • 2011-12-20
      • 2012-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-21
      相关资源
      最近更新 更多