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






3.93/5 (6投票s)
2006年8月17日
3分钟阅读

60356

703
扩展的DropDownList控件,允许在不回发的情况下搜索项目。
引言
在我参与的一个项目中,我们遇到了在DropDownList
控件中表示大量(超过20000)不同实体的问题。如果所有项目都绑定到控件,页面大小可能会超过2-3 MB。当然,这是不可接受的,于是我们想出了为DropDownList
控件添加搜索功能的解决方案。这样,用户可以指定搜索条件并减少项目数量。同时,也带来了一些其他问题,例如如何减少回发次数,以及如何使搜索控件紧凑且舒适。上述所有问题都可以通过结合使用回调和JavaScript来解决。
背景
当控件绑定到数据源时,它会检查其中的对象数量。如果绑定对象的数量超过了定义的限制,控件将按照第一张图所示的方式进行渲染。当用户按下 按钮时,用于输入搜索查询的表单会出现在下拉列表附近。此表单显示在第二张图中。输入查询后,用户应按下
按钮,控件将向服务器发送一个回调。此回调的结果是满足指定搜索查询的项目集。在此示例中,用户指定了上限值,
DropDownList
将用小于该值的整数填充。
实现
服务器控件DDLWithSearch
继承自标准的System.Web.UI.WebControls.DropDownList
,并实现了System.Web.UI.ICallbackEventHandler
和System.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.css和images文件夹从$ProjectDir$\Styles复制到主题目录。
DDLWithSearch.js文件作为嵌入资源进行了编译,不应手动引用。
要在页面上使用此控件,您应该
- 为它触发的事件定义事件处理程序。
- 设置
DataTextField
和DataValueField
属性。
这是一个使用示例。
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日 - 文章提交。