Asp.Net MVC 扩展联想控件

本文转自: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 > 0)

                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[0].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 = 0; i < len; i++)

            {

                DataRow dr = dt.NewRow();

                dr[0] = drs[i][0];

                dr[1] = drs[i][1];

                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 > 0)

            {



                for (int i = 0; i < dt.Rows.Count; i++)

                {

                    JsonString.Append("{ ");

                    for (int j = 0; j < dt.Columns.Count; j++)

                    {

                        if (j < dt.Columns.Count - 1)

                        {

                            JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\",");

                        }

                        else if (j == dt.Columns.Count - 1)

                        {

                            JsonString.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + "\"" + dt.Rows[i][j].ToString() + "\"");

                        }

                    }



                    if (i == dt.Rows.Count - 1)

                    {

                        JsonString.Append("} ");

                    }

                    else

                    {

                        JsonString.Append("}, ");

                    }

                }



            }

            JsonString.Append("]}");

            return JsonString.ToString();

        }

        #endregion

    }
复制代码

 

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

View Code
(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>
复制代码

结果演示一:简单单列

Asp.Net MVC 扩展联想控件

结果演示二:带表头单列

Asp.Net MVC 扩展联想控件

结果演示三:带表头双列

Asp.Net MVC 扩展联想控件

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

Asp.Net MVC 扩展联想控件

 

你可能感兴趣的:(asp.net)