本文转自:http://www.cnblogs.com/bright-lin/archive/2013/02/06/MVC_SuggestBox.html

在web中,为改善用户体验,我们常会将一些文本输入框做成智能联想,以让用户更快更准确的输入内容。大概是这样的:当用户开始在文本框输入时,客户端脚本ajax向服务端发起请求,服务端从数据库读取返回数据,客户端解析数据附加在文本框的下拉div中供用户选择参考。

在MVC中我们可以通过扩展HtmlHelper来封装自己写的控件,以便在整个项目中像使用 Html.TextBox("") 一样来使用自定义控件。

扩展代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace HtmlHelperExt
{
public static class SuggestBoxExtensions
{
#region SuggestBox 联想控件 /// <summary>
/// 联想控件
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="name">name(id)</param>
/// <param name="value">value</param>
/// <param name="controller">controller</param>
/// <param name="action">action</param>
/// <param name="action">fieldName 要在下拉框显示的DataTable中的字段名</param>
/// <param name="action">callBack 当选择值后的回调脚本函数</param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static string SuggestBox(this HtmlHelper htmlHelper, string name, object value, string controller, string action,string fieldName,string callBack,IDictionary<string, object> htmlAttributes)
{ return htmlHelper.SuggestBox(name, value, controller, action,"", fieldName, fieldName, "", "", "",callBack, htmlAttributes);
} /// <summary>
/// 联想控件
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="name">name(id)</param>
/// <param name="value">value</param>
/// <param name="controller">controller</param>
/// <param name="action">action</param>
/// <param name="headerText">下拉选框的头部文字(要显示多列用 ';'隔开)</param>
/// <param name="displayFields">要在下拉框显示的DataTable中的字段名(要显示多列用 ';'隔开)</param>
/// <param name="valueField">要赋文本框的字段(只能是一个,且包含在displayFields中)</param>
/// <param name="action">callBack 当选择值后的回调脚本函数</param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static string SuggestBox(this HtmlHelper htmlHelper, string name, object value, string controller, string action, string headerText, string displayFields, string valueField, string callBack, IDictionary<string, object> htmlAttributes)
{ return htmlHelper.SuggestBox(name, value, controller, action, headerText, displayFields, valueField, "", "", "",callBack, htmlAttributes);
}
/// <summary>
/// 联想控件
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="name">name(id)</param>
/// <param name="value">value</param>
/// <param name="controller">controller</param>
/// <param name="action">action</param>
/// <param name="headerText">下拉选框的头部文字(要显示多列用 ';'隔开)</param>
/// <param name="displayFields">要在下拉框显示的DataTable中的字段名(要显示多列用 ';'隔开)</param>
/// <param name="valueField">要赋文本框的字段(只能是一个,且包含在displayFields中)</param>
/// <param name="keyField">选择行的主键</param>
/// <param name="keyTextBoxName">将主键值保存在以此命名的隐藏的文本控件中,可供其他地方使用</param>
/// <param name="keyTextBoxValue">初始化时主键文本控件中的值</param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static string SuggestBox(this HtmlHelper htmlHelper, string name, object value, string controller, string action, string headerText, string displayFields, string valueField, string keyField, string keyTextBoxName, string keyTextBoxValue,string callBack,IDictionary<string, object> htmlAttributes)
{
var sb = new StringBuilder(); if (htmlAttributes == null)
htmlAttributes = new Dictionary<string, object>(); string styleStr = "";
if (htmlAttributes.ContainsKey("style"))
styleStr = htmlAttributes["style"].ToString();
string boxId = name.ToUpper() + "_SUGBOX";
if (styleStr.Length > )
sb.Append(htmlHelper.TextBox(name, value, new { style = styleStr, autocomplete = "off" }));
else
sb.Append(htmlHelper.TextBox(name, value, new { autocomplete = "off" })); sb.Append("<script type=\"text/javascript\">"); sb.AppendFormat("$('{0}').suggest({{boxId:'{1}',controller:'{2}',action:'{3}',headerText:'{4}',displayFields:'{5}',valueField:'{6}',keyField:'{7}',keyTextBoxName:'{8}',callBack:'{9}'}})", "#" + name, boxId, controller, action, headerText, displayFields, valueField, keyField, keyTextBoxName,callBack); sb.Append("</script>");
if (keyTextBoxName != "")
{
sb.Append(htmlHelper.Hidden(keyTextBoxName, keyTextBoxValue));
}
return sb.ToString();
}
#endregion
}
}

通过Controller读取、解析、返回数据。将从数据库(或XML)读取的数据存入DataTable,然后转换为Json字符串再返回给客户端。本Demo中模拟数据在XML文件中。

Controller代码如下:

 public class SuggestBoxController : Controller
{
public ActionResult Demo()
{
return View();
}
public string Suggest()
{
string searchText = "";
if (Request["param"] == null)
{
return "";
}
searchText = Request["param"].ToString();
DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("~/KeyWords.xml"));
DataRow[] drs = ds.Tables[].Select("name like '%" + searchText + "%'");
DataTable dt = new DataTable();
dt.Columns.AddRange(new DataColumn[] { new DataColumn("id"), new DataColumn("name") });
int len = drs.Length; for (int i = ; i < len; i++)
{
DataRow dr = dt.NewRow();
dr[] = drs[i][];
dr[] = drs[i][];
dt.Rows.Add(dr);
} return CreateJsonStr(dt);
} #region CreateJsonStr
/// <summary>
/// 将DataTable数据转换为Json字符串
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public string CreateJsonStr(DataTable dt)
{ StringBuilder JsonString = new StringBuilder();
JsonString.Append("{ ");
JsonString.Append("\"Data\":[ ");
if (dt != null && dt.Rows.Count > )
{ for (int i = ; i < dt.Rows.Count; i++)
{
JsonString.Append("{ ");
for (int j = ; j < dt.Columns.Count; j++)
{
if (j < dt.Columns.Count - )
{
JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\",");
}
else if (j == dt.Columns.Count - )
{
JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\"");
}
} if (i == dt.Rows.Count - )
{
JsonString.Append("} ");
}
else
{
JsonString.Append("}, ");
}
} }
JsonString.Append("]}");
return JsonString.ToString();
}
#endregion
}

主要核心还是在客户端的脚本中,脚本通过ajax访问服务端,并加载绑定返回数据,响应反馈用户的操作。

(function($) {
var itemIndex = 0;
$.fn.suggest = function(options) {
var params = {
boxId: "suggestBox",
boxWidth: 250,
boxHeight: 200,
controller: "",
action: "",
headerText: "",
displayFields: "",
valueField: "",
keyField: "",
keyTextBoxName: "",
callBack: ""
};
var ops = $.extend(params, options);
var headerTextArr = new Array();
var displayFieldsArr = new Array();
headerTextArr = ops.headerText.split(';');
displayFieldsArr = ops.displayFields.split(';');
var headerStr = "";
var headerLen = headerTextArr.length; if (headerLen == 1 || headerLen == 0) {
var textBox = $(this); ops.boxWidth = textBox.css("width"); } var box = '';
if (ops.headerText.length == 0) { box = '<div id="' + ops.boxId + '" style="display:none;width:' + ops.boxWidth + ';height:' + ops.boxHeight + '"><ul class="suggestBoxItems"></ul></div>'; }
else {
for (var i = 0; i < headerLen; i++) {
if (i == headerLen - 1) {
headerStr += '<span class="headerTextShort">' + headerTextArr[i] + '</span>'
}
else {
headerStr += '<span class="headerTextLong">' + headerTextArr[i] + '</span>'
}
}
box = '<div id="' + ops.boxId + '" style="display:none;width:' + ops.boxWidth + ';height:' + ops.boxHeight + '"><div class = "headerText">' + headerStr + '</div><ul class="suggestBoxItems"></ul></div>'; }
$(this).after(box); var itemCount = 0;
$(this).bind('keyup', function(e) {
var value = $.trim($(this).val());
if (value.length >= 1) {
var position = $(this).position(); $('#' + ops.boxId).css({ 'display': 'block', 'background': 'white', 'color': 'black', 'position': 'absolute', 'border': "1px solid #D5D5D5", 'left': position.left, 'top': position.top + 22 });
var pVal = $(this).val() + "";
if (pVal.search('&') >= 0) {
pVal = pVal.replace('&', '%26');
}
if (e.keyCode != 38 && e.keyCode != 40 && e.keyCode != 13 && e.keyCode != 9) {
var sugTextBox = $(this);
var dataUrl = "/" + ops.controller + "/" + ops.action; if (pVal != "") {
$.ajax({
type: "post",
async: true,
url: dataUrl,
data: "param=" + pVal,
dataType: "json",
cache: false,
timeout: 5000,
beforeSend: loading(ops.boxId),
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
$('#' + ops.boxId).slideUp("slow");
$('#' + ops.boxId + ' ul').html('');
},
success: function(data) {
initBox(ops.boxId, sugTextBox, data, displayFieldsArr, ops.valueField, ops.keyField, ops.keyTextBoxName);
} });
} itemIndex = 0;
}
var itemCount = $('#' + ops.boxId + ' ul li').length;
switch (e.keyCode) {
case 38:
if (itemIndex == 0) {
itemIndex = itemCount + 1;
}
if (itemIndex > 1) {
$('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').css({ 'background': 'white', 'color': 'black' });
itemIndex--;
} $('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').css({ 'background': '#7AADEB', 'color': 'white' });
$(this).val($('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').find('font').text());
if (ops.keyTextBoxName != "") {
$('#' + ops.keyTextBoxName).val($('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').find('div').text());
}
break;
case 40:
if (itemIndex < itemCount) {
$('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').css({ 'background': 'white', 'color': 'black' });
itemIndex++;
} $('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').css({ 'background': '#7AADEB', 'color': 'white' });
$(this).val($('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').find('font').text());
if (ops.keyTextBoxName != "") {
$('#' + ops.keyTextBoxName).val($('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').find('div').text());
}
break;
case 13:
if (itemIndex > 0 && itemIndex <= itemCount) {
$(this).val($('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').find('font').text());
if (ops.keyTextBoxName != "") {
$('#' + ops.keyTextBoxName).val($('#' + ops.boxId + ' ul li:nth-child(' + itemIndex + ')').find('div').text());
}
$('#' + ops.boxId).slideUp("fast");
$('#' + ops.boxId + ' ul').html('');
eval(ops.callBack);
}
break;
default:
break;
}
}
else { $('#' + ops.boxId).slideUp("fast");
$('#' + ops.boxId + ' ul').html('');
}
});
$(this).blur(function() {
var tempLi = $('#' + ops.boxId + ' ul li:nth-child(1)'); if (itemIndex == 0 && tempLi != undefined) { $(this).val(tempLi.find('font').text());
if (ops.keyTextBoxName != "") {
$('#' + ops.keyTextBoxName).val(tempLi.find('div').text());
}
itemIndex = 1;
}
if ($('#' + ops.boxId + ' ul').html() != '') {
eval(ops.callBack);
} $('#' + ops.boxId).slideUp("fast");
$('#' + ops.boxId + ' ul').html('');
}); }; function loading(boxId) {
$('#' + boxId + ' ul').html('<img alt="loading" src="/Scripts/SuggestBox/loading.gif"/>'); }
function initBox(boxId, obj, data, displayFieldsArr, valueField, keyField, keyTextBoxName) { var str = "";
if (data == undefined || data.Data == undefined || data.Data.length == 0) {
$('#' + boxId + ' ul').html('<div class="noRecordsTip">No records found<div>');
}
else { for (var i = 0; i < data.Data.length; i++) {
var fieldStr = "";
for (var j = 0; j < displayFieldsArr.length; j++) {
if (displayFieldsArr[j] == valueField) {
if (j == 0 || j != displayFieldsArr.length - 1) {
fieldStr += "<font class='singleField'>" + data.Data[i][displayFieldsArr[j]] + "</font>";
}
else {
fieldStr += "<font>" + data.Data[i][displayFieldsArr[j]] + "</font>"; }
}
else {
var tempValue = data.Data[i][displayFieldsArr[j]]; if (tempValue.length > 16) {
tempValue = tempValue.substr(0, 16) + "...";
}
fieldStr += "<span class='commonFields'>" + tempValue + "</span>";
}
}
if (keyField != "") {
fieldStr += "<div style = 'display:none;'>" + data.Data[i][keyField] + "</div>";
} str += "<li>" + fieldStr + "</li>";
}
$('#' + boxId + ' ul').html(str);
} if (data != undefined && data.Data != undefined && data.Data.length == 1) {
var tempLi = $('#' + boxId + ' ul li');
obj.val(tempLi.find('font').text());
if (keyTextBoxName != "") {
$('#' + keyTextBoxName).val(tempLi.find('div').text());
}
itemIndex = 1;
}
$('#' + boxId + ' ul li').each(function() {
$(this).bind('click', function() {
obj.val($(this).find('font').text());
if (keyTextBoxName != "") {
$('#' + keyTextBoxName).val($(this).find('div').text());
}
eval(ops.callBack);
$('#' + boxId).slideUp("fast"); });
}); $('#' + boxId + ' ul li').each(function() {
$(this).hover(
function() {
$('#' + boxId + ' ul li:nth-child(' + itemIndex + ')').css({ 'background': 'white', 'color': 'black' });
itemIndex = $('#' + boxId + ' ul li').index($(this)[0]) + 1;
$(this).css({ 'background': '#7AADEB', 'color': 'white' });
obj.val($(this).find('font').text());
if (keyTextBoxName != "") {
$('#' + keyTextBoxName).val($(this).find('div').text());
} },
function() {
$(this).css({ 'background': 'white', 'color': 'black' });
}
);
});
};
})(jQuery);

在View里面需要Import我们写的扩展类所在的命名空间,<%@ Import Namespace="HtmlHelperExt" %>

以及引入相关的js、css(extension.suggestbox.js 和 jquery-1.4.1.js 和 SugBoxStyle.css)

<div>
<% IDictionary<string, object> htmlAttributes = new Dictionary<string, object>(); htmlAttributes.Add("style", "width:198px"); %> 简单单列 <%=Html.SuggestBox("MySuggestBox01", "", "SuggestBox", "Suggest","name","", htmlAttributes)%>
<br /><br /><br /><br />
带表头单列 <%=Html.SuggestBox("MySuggestBox02", "", "SuggestBox", "Suggest","关键字","name","name","", htmlAttributes)%> <br /><br /><br /><br />
带表头双列 <%=Html.SuggestBox("MySuggestBox03", "", "SuggestBox", "Suggest","编号;关键字","id;name","name","", htmlAttributes)%> <br /><br /><br /><br />
带表头双列+回调函数 <%=Html.SuggestBox("MySuggestBox04", "", "SuggestBox", "Suggest", "编号;关键字", "id;name", "name", "id", "MySuggestBox04_ID", "0", "afterSelect()", htmlAttributes)%>
<span id="tip"></span> <script type="text/javascript">
var id = document.getElementById("MySuggestBox04_ID").value;
document.getElementById("tip").innerHTML = "当前选择的编号是: <font color='red'>" + id + "</font>";
function afterSelect() { var id = document.getElementById("MySuggestBox04_ID").value;
document.getElementById("tip").innerHTML = "当前选择的编号是: <font color='red'>"+id+"</font>"; }
</script>
</div>

结果演示一:简单单列

结果演示二:带表头单列

结果演示三:带表头双列

结果演示四:带表头双列+回调函数(选择一值时将key值赋给指定的Hidden中)

[转]Asp.Net MVC 扩展联想控件的更多相关文章

  1. ASP.NET MVC显示UserControl控件(扩展篇)

    昨晚Insus.NET有怀旧一下<念念不忘,ASP.NET MVC显示WebForm网页或UserControl控件>http://www.cnblogs.com/insus/p/3641 ...

  2. 基于存储过程的MVC开源分页控件--LYB.NET.SPPager

    摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件MVCPager(http://www.webdiyer.com/)算 ...

  3. 基于存储过程的MVC开源分页控件

    基于存储过程的MVC开源分页控件--LYB.NET.SPPager 摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件M ...

  4. 验证控件插图扩展控件ValidatorCalloutExtender(用于扩展验证控件)和TextBoxWatermarkExtender

    <asp:ScriptManager ID="ScriptManager1" runat="server">  </asp:ScriptMan ...

  5. 《ASP.NET1200例》ListView 控件与DataPager控件的结合<二>

    ASP.NET使用ListView数据绑定控件和DataPager实现数据分页显示 为什么使用ListView+DataPager的方式实现分页显示? .net提供的诸多数据绑定控件,每一种都有它自己 ...

  6. ASP.NET服务端基本控件介绍

    ASP.NET服务端基本控件介绍 大概分为三种控件: HTML控件,ASP.NET把HTML控件当成普通字符串渲染到浏览器端,不去检查正确性,无法在服务端进行处理ASP.NET服务端控件,经过ASP. ...

  7. asp.net学习之数据绑定控件、数据源控件概述

    原文:asp.net学习之数据绑定控件.数据源控件概述 1.asp.net数据绑定控件分为三大类,每个类分别进行详细:      ● 列表式数据绑定控件: 列表式数据绑定控件常用来在一个表格内的一个字 ...

  8. asp.netajax开发应用心得-accordation控件的事件处理

    今天,再次运行以前的项目时,发现按钮的单击事件不起作用了,加了断点之后发现根本没有触发该事件.... 按照网上找到的答案,有的说把控件删掉重新拖拽一个进去,虽然以前也遇到过控件失效,重新拖拽有效的时候 ...

  9. (转)客户端触发Asp.net中服务端控件事件

    第一章. Asp.net中服务端控件事件是如何触发的 Asp.net 中在客户端触发服务端事件分为两种情况: 一. WebControls中的Button 和HtmlControls中的Type为su ...

随机推荐

  1. 第二百九十六天 how can I 坚持

    今天果真好冷,至今遇到的最冷的一天,出去一趟,脸都快要冻瘫了. 感觉自己事真的多,找个对象还这事那事的,活该单身. 好愁人啊. 今天,魏中贺来北京,本来说的要明天聚聚,可是,都不给力啊,都不知道在忙啥 ...

  2. 【转】 Nginx系列(一)--nginx是什么?

    原博文出于:http://blog.csdn.net/liutengteng130/article/details/46700939  感谢! 一.介绍 Nginx是一个高性能的HTTP和反向代理服务 ...

  3. Java设计模式系列之责任链模式

    责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道 ...

  4. POJ 1151 Atlantis (扫描线+线段树)

    题目链接:http://poj.org/problem?id=1151 题意是平面上给你n个矩形,让你求矩形的面积并. 首先学一下什么是扫描线:http://www.cnblogs.com/scau2 ...

  5. HDU 5521 Meeting (最短路,dijstra)

    题意:有N个点,两个人,其中一个人住在点1,另一个人住在点n,有M个点集,集合内的数表示任意两点的距离为dis ,现在问,如果两个人要见面, 需要最短距离是多少,有哪几个点能被当成见面点. 析:分别对 ...

  6. ActiveMQ学习笔记(二) JMS与Spring

    上文可见,JMS Native API使用起来不是特别方便.好在Spring提供了很好的JMS支持. (一)配置ConnectionFactory 如果使用连接池的话,不要忘记activemq-poo ...

  7. [原]使用node-mapnik和openstreetmap数据初步搭建瓦片服务

    最近依然还是有点小忙,只能挤点时间来学习点,先解决有没有的问题,再解决好不好的问题:) 本文将承接上文<使用node-mapnik生成openstreetmap-carto风格的瓦片>的内 ...

  8. 一个 C# 获取高精度时间类(调用API QueryP*)

    如果你觉得用 DotNet 自带的 DateTime 获取的时间精度不够,解决的方法是通过调用 QueryPerformanceFrequency 和 QueryPerformanceCounter这 ...

  9. C#获取当前应用程序所在路径及环境变量

    一.获取当前文件的路径 string str1=Process.GetCurrentProcess().MainModule.FileName;//可获得当前执行的exe的文件名. string st ...

  10. 有关static静态修饰符的学习心得

    初学java,面对着这个static修饰符,愣是琢磨了两天时间,还在今天琢磨透了,现在将悟到的东西记录下来: 1.static修饰符表示静态修饰符,其所修饰的内容(变量.方法.代码块暂时学到这三种)统 ...