8

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

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

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

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

任何人都可以建议我如何确保每次回发都调用我的自定义 ListBox 的 LoadPostData() 方法,而不管用户是否进行了选择?

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

大卫

4

3 回答 3

8

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

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

在 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 个隐藏字段来跟踪不同的事件,但概念是相同的)

希望这对将来的其他人有所帮助……</p>

于 2011-02-25T13:44:32.553 回答
3

仅在选择项目时才工作听起来真的很奇怪。检查是否正在调用 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”。

于 2010-07-09T09:45:16.333 回答
1

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

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

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

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

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

于 2010-07-09T09:55:28.903 回答