前段時間,寫一個使用者部門的管理頁面,需要對後台擷取的使用者資料實現英漢檢索功能。
同時,選定一個選項之後,需要觸發事件與後台互動,將該使用者所在的部門顯示到頁面右邊的ListBox控制項中。
一、Dojo的FilteringSelect組件實現拼音檢索功能
在網上有不少相關的介紹,其中比較經典的有"海盜亂語"的關於重寫Dojo的FilteringSelect組件實現拼音檢索功能的介紹(地址http://cosbor.web-144.com/?p=38、http://cosbor.web-144.com/?p=52)。由於作者的Demo後台以及pinyin4j的jar包都是基於Java平台的,本人花了一點時間將其實現在.Net平台下,並成功的實現了FilteringSelect選中事件的註冊。實現原理請詳細參考"海盜亂語"部落格中的分析,這裡對.Net平台下的實現思路做簡要說明,並貼出源碼供大家參考(在此對作者提供的思路表示感謝!):
首先,引入Dojo工具包,在dojo目錄下添加一個"test"檔案夾,建立一個FilteringSelect.js檔案,如:
FilteringSelect.js檔案的作用是重寫FilteringSelect組件,"海盜亂語"的博文中給出了的代碼清單,為方便起見轉貼如下:
複製代碼 代碼如下:define([
"dojo/_base/declare", // declare,
"dojo/dom-attr", // domAttr.get
"dijit/form/FilteringSelect"
], function(declare, domAttr ,FilteringSelect){
return declare("test.FilteringSelect", [FilteringSelect], {
displayValueAttr:null, //新增一個自訂屬性,用於指定FilteringSelect的textbox中最終顯示內容的屬性欄位
// summary:
// 覆蓋dijit.form._AutoCompleterMixin的同名方法,使FilteringSelect支援displayValueAttr指定textbox最終顯示內容,而不是預設顯示searchAttr指定的欄位內容
_announceOption: function(/*Node*/ node){
if(!node){
return;
}
// pull the text value from the item attached to the DOM node
var newValue;
if(node == this.dropDown.nextButton ||
node == this.dropDown.previousButton){
newValue = node.innerHTML;
this.item = undefined;
this.value = '';
}else{
var item = this.dropDown.items[node.getAttribute("item")];
var displayAttr = this.displayValueAttr!=null?this.displayValueAttr:this.searchAttr;//此處判斷是否配置了自訂屬性displayValueAttr
newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
this.store.getValue(item, displayAttr) : item[displayAttr]).toString();//將this.searchAttr替換為displayAttr
this.set('item', item, false, newValue);
}
// get the text that the user manually entered (cut off autocompleted text)
this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
// set up ARIA activedescendant
this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id"));
// autocomplete the rest of the option to announce change
this._autoCompleteText(newValue);
},
});
});
然後,建立一個WebForm頁面,放置一個FilteringSelect控制項,資料來源取值為頁面類繼承過來的userListstr欄位,頁面前台代碼如下: 複製代碼 代碼如下:<%@ Page Title="" Language="C#" AutoEventWireup="true" CodeFile="OrgRelation.aspx.cs" Inherits="OrgRelation" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
<script src="Scripts/jquery-1.4.1-vsdoc.js" type="text/javascript"></script>
<link href="Scripts/dojo/dijit/themes/claro/claro.css" rel="stylesheet" type="text/css" />
<link href="Scripts/dojo/dojo/resources/dojo.css" rel="stylesheet" type="text/css" />
<script src="Scripts/dojo/dojo/dojo.js" type="text/javascript"></script>
<script type="text/javascript">
//參數設定
require([
'test/FilteringSelect',
'dojo/store/Memory',
'dojo/domReady!'
], function (FilteringSelect, Memory) {
var jsonstr = '<%=userListStr%>';
var json = jQuery.parseJSON(jsonstr);
var obj = {data:""};
obj['data'] = json;
var selectStore = new Memory(obj);
//建立FilteringSelect
var testSelect = new FilteringSelect({
id: "testSelect",
name: "test",
value: "",
store: selectStore,
searchAttr: 'py', //指定輸入文字框進行用來進行檢索的欄位
labelAttr: 'name', //指定下拉式功能表中顯示的欄位
displayValueAttr: 'name', //指定選中下拉式功能表後顯示在輸入框中的欄位
required: false,
autoComplete: false
}, "testSelect");
});
//註冊失去焦時間點事件
window.onload = function () {
function selblur() {
var guid = dijit.byId('testSelect').attr('value');
alert(guid);
window.location.href = "OrgRelation.aspx?userId=" + guid;
return false;
}
var sel = dojo.byId("testSelect");
dojo.connect(sel, "onblur", selblur);
};
</script>
</head>
<body>
<form id="Form1" method="post" runat="server">
<div align="center" id="title">
<strong>編輯使用者部門關係</strong>
</div>
<div style="text-align: center;width: 100%;padding-top: 100px;font-size:15px;">選擇使用者:<input id="testSelect"/>
</div>
</form>
</body>
</html>
最後,在頁面載入事件中擷取使用者資料,序列化之後,賦給protected類型的userListstr欄位。其中這裡引用到微軟提供的擷取漢字拼音的類庫ChnCharInfo.dll,代碼請單如下:複製代碼 代碼如下:using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.International.Converters.PinYinConverter;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
public partial class OrgRelation : System.Web.UI.Page
{
protected string userListStr = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
GetUsers();
}
}
//與前台頁面Json對象格式對應的類
public class UserInfo
{
public string name { get; set; }
public string id { get; set; }
public string py { get; set; }
}
protected void GetUsers()
{
//擷取使用者資訊,及每項記錄的拼音簡碼
List<User> list =new BLL.User().GetUsers();
List<UserInfo> UserInfoList = new List<UserInfo>();
foreach (User item in list)
{
UserInfo userInfo= new UserInfo();
userInfo.id = item.UserId;
userInfo.name = item.User Name;
userInfo.py = GetPY(item.UserName);
UserInfoList .Add(userInfo);
}
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
//執行序列化 並賦值
userListStr = jsonSerializer.Serialize(UserInfoList);
}
#region 拼音檢索的相關方法
/// <summary>
/// 獲得一個漢字字元的拼音的字串集合,並處理聲調和空值
/// </summary>
/// <param name="ch">漢字字元</param>
/// <returns></returns>
public static List<string> GetPinyins(char ch)
{
List<string> list = new List<string>();
ChineseChar cc = new ChineseChar(ch); //獲得包含漢字資訊的對象
foreach (string item in cc.Pinyins)
{
if (item != null)
{
string temp = item.Substring(0, item.Length - 1);
if (!list.Contains(temp))
{
list.Add(temp);
}
}
}
return list;
}
/// <summary>
/// 得到一個片語的拼音的首字母字串(多音取第一個)
/// </summary>
/// <returns></returns>
public static string GetPY(string str)
{
Regex reg = new Regex(@"[\u4e00-\u9fa5]");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
string ch = str[i].ToString();
if (reg.IsMatch(ch))
{
string s = GetPinyins(str[i])[0];
sb.Append(s[0]);
}
else
{
sb.Append(ch);
}
}
return sb.ToString();
}
#endregion
}
這樣拼音檢索的功能就完成了。不過有兩點不盡人意的地方:1.使用拼音檢索後,不好再使用中文檢索,2.網上查了很久,沒有選中項改變事件介紹,代碼中只是註冊了一個失去焦時間點事件,在與後台互動方面不太方便(可能是本人對dojo事件不熟,歡迎對dojo api有研究的大俠指點)。
二、JqueryUI的autocomplete外掛程式實現拼音檢索功能
其實JqueryUI也提供了一個非常好用的外掛程式--autocomplete,它與ChnCharInfo.dll類庫配合使用,不僅能實現同樣優秀的檢索功能,而且能夠很好的解決上述兩個問題。不妨來看看:
需要用到的相關組件和引用的類庫:Jquery-UI 、漢字拼音轉換語言套件類庫ChnCharInfo .dll和Json對象序列化類別庫Newtonsoft.Json.dll,如下所示:
1.WebForm的aspx頁面實現:
首先引入jquery-1.8.2.js、jquery-ui-1.9.0.custom.js、jquery-ui-1.9.0.custom.css,然後在頁面載入完成的事件中寫如下指令碼:
複製代碼 代碼如下:<script type="text/javascript">
$(function () {
$("#selCompate").autocomplete({
source: "GetUser.ashx",
minLength: 1,
//以下為選中事件
select: function (event, ui) {
temp = ui.item;
$("#hidcontactid").val(temp.id);
$("#hidcontactname").val(temp.label);
$("#form2").attr("action", "./OrgRelation.aspx?contactId=" + temp.id + "&contactName=" + temp.label);
$("#form2").submit();
}
});
$("#selCompate").val($("#hidcontactname").val())
});
</script>
其中第4行的 source: "GetUser.ashx",是指鍵入字元後,發送非同步請求的地址,GetUser.ashx負責向請求的用戶端提供滿足Json格式的使用者資訊;第5行的minLength: 是輸入到幾個字元時開始發送非同步請求;第7行的select: function(event, ui){}即選中事件,ui.item表示被選中的項;第8-9行的隱藏欄位存值,是為了頁面重新整理後能重新擷取該選中項的相關資訊,重新寫回頁面以備用;第10-11行以當前選中項的id和label被為參數向OrgRelation.aspx發送post請求,是實現將選中使用者的所在部門查詢處來,顯示到頁面右側的ListBox控制項中,其服務端實現與本次討論的內容無關,代碼就不貼出來了。
頁面的完整代碼清單如下:
複製代碼 代碼如下:<%@ Page Title="" Language="C#" AutoEventWireup="true" CodeFile="OrgRelation.aspx.cs"
Inherits="OrgRelation"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
<script src="Scripts/jquery-1.8.2.js" type="text/javascript"></script>
<script src="Scripts/jquery-ui-1.9.0.custom.js" type="text/javascript"></script>
<link href="css/ui-lightness/jquery-ui-1.9.0.custom.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
$(function () {
$("#selCompate").autocomplete({
source: "GetUser.ashx",
minLength: 1,
//以下為選中事件
select: function (event, ui) {
temp = ui.item;
$("#hidUserId").val(temp.id);
$("#hidUserName").val(temp.label);
$("#form2").attr("action", "./OrgRelation.aspx?UserId=" + temp.id + "&UserName=" + temp.label);
$("#form2").submit();
}
});
$("#selCompate").val($("#hidUserName").val())
});
</script>
</head>
<body>
<form id="form2" method="post" action="./OrgRelation.aspx" name="sendForm"></form>
<form id="Form1" method="post" runat="server" >
<input type="hidden" id="hidUserId" name="hidUserId" value="<%=currentUserId%>" />
<input type="hidden" id="hidUserName" name="hidUserName" value="<%=currentUserName%>"/>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div id="outline">
<div align="center" id="title">
<strong>編輯使用者部門關係</strong>
</div>
<div id="main">
<table align="center">
<tr>
<td>
<div>
<b>選擇使用者:</b>
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<input type="text" id="selCompate" style="line-height: 10px; margin-top: 0px;
margin-left: 0px; height: 23px;"
runat="server" />
</ContentTemplate>
</asp:UpdatePanel><br />
<b>選擇部門:</b>
</div>
<br />
</td>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td valign="top" width="41%">
<div id="left">
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TreeView ID="TreeViewOrgData" runat="server" Font-Names="微軟雅黑" Height="385px"
Font-Size="11pt" ForeColor="Black" BackColor="AliceBlue" OnTreeNodeCollapsed="TreeViewOrgData_TreeNodeCollapsed"
OnTreeNodeExpanded="TreeViewOrgData_TreeNodeExpanded" ShowCheckBoxes="All">
</asp:TreeView>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</td>
<td align="center" valign="middle" width="18%">
<p>
<asp:Button ID="btnAddOrg" runat="server" Width="85px" Text="添加部門 >>" Height="22px"
BorderStyle="Solid" BorderColor="DarkGray" BackColor="GhostWhite" Font-Size="9pt"
OnClick="btnAddOrg_Click"></asp:Button></p>
<p>
<asp:Button ID="btnRemoveOrg" runat="server" Width="85px" Text="<< 移除部門" Height="22px"
BorderStyle="Solid" BorderColor="DarkGray" BackColor="GhostWhite" Font-Size="9pt"
OnClick="btnRemoveOrg_Click"></asp:Button></p>
</td>
<td valign="top" align="center" width="41%">
<div id="right">
<asp:ListBox ID="LBOrg" runat="server" Width="300px" Height="350px" Rows="13" BackColor="AliceBlue"
Font-Size="11pt" SelectionMode="Multiple" >
</asp:ListBox>
</div>
</td>
</tr>
</table>
</div>
<br />
<div align="center" id="bottom">
<asp:Button ID="btnBack" runat="server" Text="·返 回·" BackColor="#f8f8ff" />
<asp:Button ID="btnSave" runat="server" Text="·保 存·" BackColor="#f8f8ff"
onclick="btnSave_Click" />
</div>
</div>
</form>
</body>
</html>
2.Global.asax中使用者資料的準備
由於這裡的使用者資料不經常變化,考慮到搜尋是需要頻繁的向服務端請求資料,因此將使用者資料存入了Application中,這樣搜尋時直接從Application中取,不用每次去資料庫查詢。
Application對象的賦值是在全域應用程式Global.asax的 Application_Start事件中完成的,代碼如下:
複製代碼 代碼如下:void Application_Start(object sender, EventArgs e)
{
Application.Lock();
Application["User"] = GetUsers();
Application.UnLock();
}
擷取使用者資訊的GetUser方法中,同時完成了拼音簡碼的擷取,代碼如下:複製代碼 代碼如下:protected List<string> GetUsers()
{
List<Model.User> list = new BLL.User().GetUsers();
List<string> UserList = new List<string>();
foreach (Model.User item in list)
{
UserList .Add(item.Id+"|"+item.Name+"|"+GetPY(item.Name).Replace(" ","").ToLower());
}
return UserList ;
}
/// <summary>
/// 獲得一個漢字字元的拼音的字串集合,並處理聲調和空值
/// </summary>
/// <param name="ch">漢字字元</param>
/// <returns></returns>
public static List<string> GetPinyins(char ch)
{
List<string> list = new List<string>();
Microsoft.International.Converters.PinYinConverter.ChineseChar cc = new Microsoft.International.Converters.PinYinConverter.ChineseChar(ch); //獲得包含漢字資訊的對象
foreach (string item in cc.Pinyins)
{
if (item != null)
{
string temp = item.Substring(0, item.Length - 1);
if (!list.Contains(temp))
{
list.Add(temp);
}
}
}
return list;
}
/// <summary>
/// 得到一個片語的拼音的首字母字串(多音取第一個)
/// </summary>
/// <returns></returns>
public static string GetPY(string str)
{
Regex reg = new Regex(@"[\u4e00-\u9fa5]");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
string ch = str[i].ToString();
if (string.IsNullOrEmpty(ch))
{
}
else if (reg.IsMatch(ch))
{
string s = GetPinyins(str[i])[0];
sb.Append(s[0]);
}
else
{
sb.Append(ch);
}
}
return sb.ToString();
}
至於Application與資料庫中資料的一致的考慮,可提供一個一般處理常式UpdateApplication.ashx負責更新Application(代碼與Global.asax中基本相同),當資料庫發生變化時,訪問UpdateApplication.ashx即可更新Application["Contact"]對象。
3.GetUser.ashx中返回符合檢索條件的資料
GetUser.ashx中響應搜尋事件的服務端代碼清單如下:
複製代碼 代碼如下:<%@ WebHandler Language="C#" Class="GetUser" %>
using System;
using System.Web;
using BLL;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using Microsoft.International.Converters.PinYinConverter;
public class GetUser :JavaScriptSerializer,IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
int i = 0;
List<string> strlist = context.Application["User"] as List<string>;
string inputStr = context.Request.QueryString.Get("term").ToLower();
List<UserItem> userList = new List<UserItem>();
foreach (string str in strlist)
{
string[] userArr = str.Split('|');
if (i < 10)
{
Regex reg = new Regex(@"^" + inputStr);
if (reg.IsMatch(userArr[2]) || reg.IsMatch(userArr[1]))
{
UserItem item = new UserItem();
item.id = userArr[0];
item.label = userArr[1];
item.value = userArr[2];
userList.Add(item);
i++;
}
}
else
{
break;
}
}
context.Response.ContentType = "application/json";
string output = Newtonsoft.Json.JsonConvert.SerializeObject(userList);
context.Response.Write(output);
}
public bool IsReusable
{
get
{
return false;
}
}
}
public class UserItem
{
public string id { get; set; }
public string label { get; set; }
public string value { get; set; }
}
第17行是擷取文字框中輸入的檢索字串inputstr,這裡使用Regex對擷取名稱以inputstr開頭的記錄(中文檢索)或者拼音簡碼以inputstr開頭的記錄(拼音檢索)。如果需要模糊檢索功能,可以修改第25行的Regex為:Regex reg = new Regex(inputStr);即可。如果需要更多欄位的複合檢索(例如使用者手機號,郵箱地址等),也只要Application對像賦值時擷取相關的欄位資訊,在26行的if判斷中增加相應匹配項即可。
其中UserItem是為頁面提供Json對象的類,label是必須欄位,搜尋方塊中顯示的內容即該欄位的值。得到合格資料集合後,需要使用Newtonsoft.Json.JsonConvert的SerializeObject方法進行序列化,再返回給用戶端。
到此,即實現了本文開篇的貼圖效果。