让AutoCompleteExtender支持选中项的Id
在使用AutoCompleteExtender控件时,有时候我们希望同时返回选中项的文本和Id号,即支持键值对数据,比如利用AutoCompleteExtender选择员工时,选中一个员工姓名的同时,也将员工的Id号返回,这样我们就可以直接使用员工Id,下面介绍两种方法(本文使用.Net 2.0 版本说明具体步骤):
第一种方式:最直接的方式
主要有3个步骤:
1、改造返回字符串数组的WebServices方法,让返回的为NameVlue键值对数据,但仍旧为字符串数组格式.
以中自带的例子来说明,原代码为:
items.Add(prefixText + c1 + c2 + c3);
改为
items.Add(prefixText + c1 + c2 + c3 + "|" + Guid.NewGuid().ToString());
这里用分隔符来分割NameVlue键值对,以方便客户端取出。
2、改造JavaScript文件中AutoCompleteBehavior类的_update方法
_update方法中的completionItems参数,就是WebServices返回的JSON数据生成的JavaScript数组。
找到_update方法中的下面这句代码
itemElement.appendChild(document.createTextNode(this._getTextWithInsertedWord(text)));
改为
if(typeof( completionItems[i]) == "string" && completionItems[i].indexOf("|") >= 0)
{
itemElement.setAttribute("title", completionItems[i].split("|")[1]);
itemElement.appendChild(document.createTextNode(this._getTextWithInsertedWord(completionItems[i]).split("|")[0]));
}
else
{
itemElement.appendChild(document.createTextNode(this._getTextWithInsertedWord(text)));
}
3、修改_setText方法,以便在选中一项suggestion的同时,获取其相应的Id
找到下面这句
element.value = text;
在此句下面加上
if($get(element.id + "Id"))
{
$get(element.id + "Id").value = item.getAttribute("title");
}
在这里,还要在aspx页面上,在AutoComplete的TextBox后面加一个隐藏域,且Id号为AutoComplete的TextBox的Id号后再加“Id”,这个可以自己任意取,同时也可以不用隐藏域,也可以通过其他方法得到选中项的Id。
第二种方式:利用AutoCompleteBehavior中_update方法的变量pair
1、改造返回字符串数组的WebServices方法,让返回的为NameVlue键值对数据
你可以用下面三个方法中的任意一个
[WebMethod]
public List<Pair> GetCompletionList2(string prefixText, int count)
{
if (count == 0)
{
count = 10;
}
if (prefixText.Equals("xyz"))
{
return new List<Pair>(0);
}
Random random = new Random();
List<Pair> items = new List<Pair>(count);
for (int i = 0; i < count; i++)
{
char c1 = (char)random.Next(65, 90);
char c2 = (char)random.Next(97, 122);
char c3 = (char)random.Next(97, 122);
items.Add( new Pair( prefixText + c1 + c2 + c3, Guid.NewGuid().ToString()));
}
return items;
}
public string[] GetCompletionList3(string prefixText, int count)
{
if (count == 0)
{
count = 10;
}
if (prefixText.Equals("xyz"))
{
return new string[0];
}
Random random = new Random();
List<string> items = new List<string>(count);
for (int i = 0; i < count; i++)
{
char c1 = (char)random.Next(65, 90);
char c2 = (char)random.Next(97, 122);
char c3 = (char)random.Next(97, 122);
items.Add(String.Format("{{\"First\":\"{0}\", \"Second\":\"{1}\"}}", prefixText + c1 + c2 + c3, Guid.NewGuid().ToString()));
}
return items.ToArray();
}
2、改造JavaScript文件中AutoCompleteBehavior类的_update方法
找到
catch (ex) {
text = completionItems[i];
value = completionItems[i];
}
改为
catch (ex) {
try {
if (completionItems[i].First) {
text = completionItems[i].First;
value = completionItems[i].Second;
}
if (completionItems[i].Value) {
text = completionItems[i].Key;
value = completionItems[i].Value;
}
else {
text = completionItems[i];
value = completionItems[i];
}
}
catch (ex2) {}
}
3、修改_setText方法,以便在选中一项suggestion的同时,获取其相应的Id
找到下面这句
element.value = text;
在此句下面加上
if($get(element.id + "Id"))
{
$get(element.id + "Id").value = itemElement._value;
}
在这里,也是在AutoComplete的TextBox后面加一个隐藏域,当然你也可以用其他手段。
这里需要说一下服务器端的 GetCompletionList3方法,实际上返回的每一个suggestion都是一个JSON文本,在客户端的_update方法中,有句代码为
var pair = Sys.Serialization.JavaScriptSerializer.deserialize('(' + completionItems[i] + ')');
此句代码只有在服务器端返回的每一个suggestion是一个JSON文本才会正确执行,因此我们的第一种方式和第二种方式中的GetCompletionList1和GetCompletionList2,这三个WebServices方法
返回的数据都不会使此句执行,真正执行的是catch里的代码,因为这三个WebServices方法返回的数据都会导致此句代码抛出异常。只有GetCompletionList3方法方法才会导致此句代码执行。
其实客户端使用Sys.Net.WebServiceProxy.invoke返回的数据已经进行了反串行化,已经是JavaScript对象了,从Sys.Net.WebRequestExecutor的get_object()方法就可以知道。
另外提一下就是在asp.net 2.0中当使用frame时,MicrosoftAjax.js中的一个小bug,这个bug在asp.net 3.5中已经修复。
在asp.net 2.0 的 Sys.UI.DomElement 对象中有个getLocation静态方法,里面有这句 offsetL += (f.frameBorder || 1) * 2 + ...
其中f.frameBorder与页面上设置的值紧密相关,frameBorder 属性设置或返回是否显示框架周围的边框,1表示有边框,0表示没有边框。由于我是仿照着别人的界面,使用了frame,是这样写的
<frame ... frameborder="NO"> frameBorder既不是1也不是0,造成f.frameBorder为NaN,导致getLocation方法返回的是NaN。因为MicrosoftAjax.js是嵌入的资源,无法修改,只好修改AjaxControlToolkit中的Common.js文件
找到里面的getLocation方法的最后一句,改为
var returnValue = Sys.UI.DomElement.getLocation(element);
if(isNaN(returnValue.x) && Sys.Browser.agent == Sys.Browser.InternetExplorer)
{
// 这里将asp.net 3.5中MicrosoftAjax.js文件的getLocation代码复制过来,在这里不详细写了
// 找到这句var offset = (f.frameBorder === "0" || f.frameBorder === "no") ? 2 : 0;
// 替换为 var offset = (f.frameBorder === "0" || f.frameBorder === "no" || f.frameBorder === "NO" || isNaN(f.frameBorder)) ? 2 : 0;
}