65.9K
CodeProject 正在变化。 阅读更多。
Home

带项目搜索功能的DropDownList控件,使用回调

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.93/5 (6投票s)

2006年8月17日

3分钟阅读

viewsIcon

60356

downloadIcon

703

扩展的DropDownList控件,允许在不回发的情况下搜索项目。

DDL with search

Search query form

Search result

引言

在我参与的一个项目中,我们遇到了在DropDownList控件中表示大量(超过20000)不同实体的问题。如果所有项目都绑定到控件,页面大小可能会超过2-3 MB。当然,这是不可接受的,于是我们想出了为DropDownList控件添加搜索功能的解决方案。这样,用户可以指定搜索条件并减少项目数量。同时,也带来了一些其他问题,例如如何减少回发次数,以及如何使搜索控件紧凑且舒适。上述所有问题都可以通过结合使用回调和JavaScript来解决。

背景

当控件绑定到数据源时,它会检查其中的对象数量。如果绑定对象的数量超过了定义的限制,控件将按照第一张图所示的方式进行渲染。当用户按下 搜索按钮 按钮时,用于输入搜索查询的表单会出现在下拉列表附近。此表单显示在第二张图中。输入查询后,用户应按下 开始按钮 按钮,控件将向服务器发送一个回调。此回调的结果是满足指定搜索查询的项目集。在此示例中,用户指定了上限值,DropDownList将用小于该值的整数填充。

实现

服务器控件DDLWithSearch继承自标准的System.Web.UI.WebControls.DropDownList,并实现了System.Web.UI.ICallbackEventHandlerSystem.Web.UI.IPostBackDataHandler等接口。在客户端,它使用DDLWithSearchCoreClass中定义的JavaScript函数。JavaScript文件被编译为作为嵌入资源包含在控件程序集中。该控件定义了一些数据操作事件

  • GetObjectsListCount - 触发以检索满足搜索条件的项列表的大小。
  • GetObjectsList - 触发以检索满足搜索条件的项列表。
  • GetObjectByID - 触发以根据ID检索对象。

以及以下属性

  • MaxRenderItemsCount - 如果绑定对象的数量超过此限制,控件将渲染搜索控件。
  • MaxCallbackItemsCount - 可以作为回调结果返回的最大项数。
  • EnableSelectItem - 启用或禁用“select...”项。
  • SelectItemText - 默认情况下,此项设置为“select...”。
  • UseSearchItemText - 默认情况下,此项设置为“please use search”。
  • NoItemsFoundItemText - 默认情况下,此项设置为“no items found”。
  • TooBigResultItemText - 默认情况下,此项设置为“too big result”。
  • ClientCallbackErrorHandler - 指定在回调过程中发生错误时调用的JavaScript函数名。
  • EnableSearch - 启用或禁用搜索控件。

由于项可以在客户端更改,因此该控件实现了System.Web.UI.IPostBackDataHandler接口。对选项集合所做的更改存储在OnPreRender方法中注册的两个隐藏输入元素中。同样,在此方法中,DDLWithSearch.js文件和回调处理程序也会被注册。

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    if (!this.Page.ClientScript.IsClientScriptBlockRegistered("DDLWithSearch"))
    {
        // Define the resource name and type.
        this.Page.ClientScript.RegisterClientScriptInclude(
                        this.GetType(), "DDLWithSearch",
                        Page.ClientScript.GetWebResourceUrl(this.GetType(),
                        "DDLWithSearch.DDLWithSearch.js"));
    }


    if ((_searchMode == SearchMode.SearchIsOn) || EnableAddItemsOnClient)
    {
        Page.ClientScript.RegisterHiddenField(string.Format("{0}{1}", 
             hiddenItemsPrefix, this.ClientID), string.Empty);
        Page.ClientScript.RegisterHiddenField(string.Format("{0}{1}", 
             hiddenSelectedPrefix, this.ClientID), string.Empty);

        string callBackRef = null;
        if(ClientCallbackErrorHandler == string.Empty)
        {
            // Without Error handle
            callBackRef = Page.ClientScript.GetCallbackEventReference(
                            // control
                            this,
                            // argument - the value of the txtSearch
                            "document.getElementById('srchTextBox').value",
                            // client call back function
                            "DDLWithSearchCore.ReceiveDdlItems",
                            // context - ddl id
                            string.Format("'{0}'", this.ClientID)
                            );
        }
        else
        {                
            //With Error handle
            callBackRef = Page.ClientScript.GetCallbackEventReference(
                                // control
                                this,
                                // argument - the value of the txtSearch
                                "document.getElementById('srchTextBox').value",
                                // client call back function
                                "DDLWithSearchCore.ReceiveDdlItems",
                                // context - ddl id
                                string.Format("'{0}'", this.ClientID),
                                ClientCallbackErrorHandler,
                                false
                                );
        }
        this.Attributes.Add(attrRunSearchStatement, 
             string.Format("javascript:DDLWithSearchCore" + 
                           ".PrepareDdlToCallBack('{0}') && {1}", 
             this.ClientID, callBackRef));
    }
}

BindItems方法会检查绑定对象的数量,并在必要时设置SearchMode.SearchIsOn模式。

public void BindItems()
{
    // get the number of objects
    int objectsCount = _GetObjectsListCount(string.Empty);

    if (objectsCount > MaxRenderItemsCount && 
        _searchSettings == SearchSettings.SearchEnabled)
    {
        _searchMode = SearchMode.SearchIsOn;

        this.Items.Clear();
        this.Items.Add(new ListItem(UseSearchItemText, this.NoneItemValue));
    }
    else
    {
        this.DataSource = _GetObjectsList(string.Empty);
        this.DataBind();

        if (EnableSelectItem)
        {
            this.Items.Insert(0, new ListItem(this.SelectItemText, 
                                              this.NoneItemValue));
        }
    }
}

GetCallbackResult方法会获取并准备满足搜索条件的项集合。

public string GetCallbackResult()
{
    ClientItemsCollection clientItemsCollection = new ClientItemsCollection();
    int objectsCount = _GetObjectsListCount(_callBackSearchQuery);

    if (objectsCount > 0)
    {
        if (objectsCount < MaxCallbackItemsCount)
        {
            IEnumerator objectsListEnumerator = 
              _GetObjectsList(_callBackSearchQuery).GetEnumerator();
            StringBuilder sb = new StringBuilder();

            if (this.EnableSelectItem)
            {
                clientItemsCollection.AddItem(this.SelectItemText, 
                                              this.NoneItemValue);
            }

            while (objectsListEnumerator.MoveNext())
            {
                PropertyInfo piDataValue = null;
                PropertyInfo piDataText = null;
                if(piDataValue == null)
                {
                    piDataValue = 
                      objectsListEnumerator.Current.GetType().GetProperty(
                      this.DataValueField, 
                      BindingFlags.Instance|BindingFlags.Public);
                    if (piDataValue == null)
                        throw new PropertyNotFoundExeption(this.DataValueField);
                    piDataText = 
                      objectsListEnumerator.Current.GetType().GetProperty(
                      this.DataTextField, BindingFlags.Instance | 
                                          BindingFlags.Public);
                    if (piDataText == null)
                        throw new PropertyNotFoundExeption(this.DataTextField);
                }
                string dataValue = 
                  piDataValue.GetValue(objectsListEnumerator.Current, 
                                       null).ToString();
                string dataText = 
                  piDataText.GetValue(objectsListEnumerator.Current, 
                                      null).ToString();
                clientItemsCollection.AddItem(dataText, dataValue);
            }
        }
        else
        {
            clientItemsCollection.AddItem(this.TooBigResultItemText, 
                                          this.NoneItemValue);
        }
    }
    else
    {
        clientItemsCollection.AddItem(this.NoItemsFoundItemText, 
                                      this.NoneItemValue);
    }
    return clientItemsCollection.GetClientString();
}

此外,SelectedValue属性也需要一些更改。

public new string SelectedValue
{
    get
    {
        return base.SelectedValue;
    }
    set
    {
        // Check if there is already such item in list
        ListItem item = Items.FindByValue(value);
        if (item == null)
        {
            object newItemObject = _GetObjectByID(value);

            if (newItemObject != null)
            {
                Type objType = newItemObject.GetType();
                PropertyInfo textFieldProperty = 
                             objType.GetProperty(this.DataTextField);
                PropertyInfo valueFieldProperty = 
                             objType.GetProperty(this.DataValueField);
                string itemText = Convert.ToString(
                     textFieldProperty.GetValue(newItemObject, null));
                string itemVal = Convert.ToString(
                     valueFieldProperty.GetValue(newItemObject, null));
                this.Items.Add(new ListItem(itemText, itemVal));
            }
            else
            {
                throw new InvalidItemValueException();
            }
        }
        base.SelectedValue = value;
    }
}

使用代码

要在您的项目中使用该控件,您应该

  • 添加对程序集DDLWithSearch.dll的引用。
  • ddlWithSearch.cssimages文件夹从$ProjectDir$\Styles复制到主题目录。

DDLWithSearch.js文件作为嵌入资源进行了编译,不应手动引用。

要在页面上使用此控件,您应该

  • 为它触发的事件定义事件处理程序。
  • 设置DataTextFieldDataValueField属性。

这是一个使用示例。

ddlTest.GetObjectsListCount += new 
  DDLWithSearch.DDLWithSearch.GetObjectsListCountEventHandler(
  ddlTest_GetObjectsListCount);
ddlTest.GetObjectsList += new 
  DDLWithSearch.DDLWithSearch.GetObjectsListEventHandler(
  ddlTest_GetObjectsList);
ddlTest.GetObjectByID += new 
  DDLWithSearch.DDLWithSearch.GetObjectByIdEventHandler(
  ddlTest_GetObjectByID);

ddlTest.DataTextField = "TestText";
ddlTest.DataValueField = "TestValue";

ddlTest.BindItems();

此使用示例在IE 6、Firefox 1.5和Opera 8.54中运行正常。

历史

  • 2006年8月9日 - 文章提交。
© . All rights reserved.