2015年9月27日 星期日

TextBox輸入文字自動完成(非WebService和使用WebService寫法整理)

如果想在TextBox上輸入文字,即時出現一個Layer文字列表,類似Google搜尋那樣
一般的做法是使用WebService吧
1.先看WebService
以個人測試Web site範例,我是開一個WebService資料夾在根目錄下,然後加入一個WebService.asmx
同時Visual Studio也會產生一份WebService.cs檔在App_Code資料夾內
WebService.cs內的程式碼:
using System.Collections;
using System.Data;
using System.Data.SqlClient;


[System.Web.Script.Services.ScriptService]
public class WebService : System.Web.Services.WebService {

    [System.Web.Services.WebMethodAttribute(), System.Web.Script.Services.ScriptMethodAttribute()]
    //方法不可宣告static
    public string[] GetCompletionList(string prefixText, int count)
    {
        //資料庫連線字串
        string connStr = @"Data Source=.\SQLEXPRESS;AttachDbFilename="
                     + System.Web.HttpContext.Current.Server.MapPath("~/App_Data/NorthwindChinese.mdf") + ";Integrated Security=True;User Instance=True";

        ArrayList array = new ArrayList();//儲存撈出來的字串集合

        using (SqlConnection conn = new SqlConnection(connStr))
        {
            DataSet ds = new DataSet();
            string selectStr = @"SELECT Top (" + count + ") CompanyName FROM Customers Where CompanyName Like '" + prefixText + "%' Order by CustomerID ASC";
            SqlDataAdapter da = new SqlDataAdapter(selectStr, conn);
            conn.Open();
            da.Fill(ds);
            foreach (DataRow dr in ds.Tables[0].Rows)
            {
                array.Add(dr["CompanyName"].ToString());
            }

        }

        return (string[])array.ToArray(typeof(string));

    } 

    
}
接著新增一個UseWebService.aspx檔案在根目錄
aspx程式碼
<%@ Page Language="C#" AutoEventWireup="true"  %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<html>
<head >
</head>
<body>
    <form id="form1" runat="server">

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

    <asp:TextBox ID="TextBox1" runat="server" />

    <asp:AutoCompleteExtender
           ID="AutoCompleteExtender1"
           runat="server"
           MinimumPrefixLength="1"
           TargetControlID="TextBox1"
           ServiceMethod="GetCompletionList"
           ServicePath="WebService/WebService.asmx"
           CompletionSetCount="15" 
    />
    <!--AutoCompleteExtender成員說明
    MinimumPrefixLength:最少輸入幾個字就呼叫Method
    TargetControlID:哪個TextBox要有自動完成功能
    ServiceMethod:呼叫的Method名
    ServicePath:WebService路徑
    CompletionSetCount:要列出幾筆資料
    -->
    </form>
</body>
</html>
結束,因為Code-Behind沒有要寫程式,所以我把UseWebService.aspx.cs宰了

2.接著再看不使用WebService的做法
先在App_Code裡新增一個AutoComplete_WebPage.cs類別
AutoComplete_WebPage.cs的程式碼
using System.Data;
using System.Data.SqlClient;
using System.Collections;

//類別繼承System.Web.UI.Page目的是為了讓其他aspx.cs可以再繼承下來使用
public class AutoComplete_WebPage :System.Web.UI.Page
{
    [System.Web.Services.WebMethodAttribute(), System.Web.Script.Services.ScriptMethodAttribute()]
    //一定要宣告成static才有效果
    public static string[] GetCompletionList(string prefixText, int count)
    {
        //連線字串
        string connStr = @"Data Source=.\SQLEXPRESS;AttachDbFilename="
                     + System.Web.HttpContext.Current.Server.MapPath("~/App_Data/NorthwindChinese.mdf") + ";Integrated Security=True;User Instance=True";

        ArrayList array = new ArrayList();//儲存撈出來的字串集合

        using (SqlConnection conn = new SqlConnection(connStr))
        {
            DataSet ds = new DataSet();
            string selectStr = @"SELECT Top (" + count + ") CompanyName FROM Customers Where CompanyName Like '" + prefixText + "%' Order by CustomerID ASC";
            SqlDataAdapter da = new SqlDataAdapter(selectStr, conn);
            conn.Open();
            da.Fill(ds);
            foreach (DataRow dr in ds.Tables[0].Rows)
            {
                array.Add(dr["CompanyName"].ToString());
            }

        }

        return (string[])array.ToArray(typeof(string));

    } 

}
接著在網站根目錄下新增一個NoWebService.aspx
NoWebService.aspx程式碼
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="NoWebService.aspx.cs" Inherits="NoWebService" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<html>
<head runat="server">
</head>
<body>
    <form id="form1" runat="server">

    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <asp:TextBox ID="TextBox1" runat="server" />

    <asp:AutoCompleteExtender
           ID="AutoCompleteExtender1"
           runat="server"
           TargetControlID="TextBox1"
           MinimumPrefixLength="1"
           ServiceMethod="GetCompletionList"
           CompletionSetCount="15" 
    />




    </form>
</body>
</html>
NoWebService.aspx.cs程式碼
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

//繼承AutoComplete_WebPage類別
public partial class NoWebService : AutoComplete_WebPage
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
}

3.最後分別把UseWebService.aspx或NoWebService.aspx執行起來,效果一樣
(註:Firefox 更新到4.x版以上可以支援ASP.net Ajax的自動完成)
整理一下兩者寫法差異

 新增項目App_Code/cs檔類別App_Code/cs檔方法網頁程式
使用WebServiceWebService.asmx、WebService.cs檔類別加[System.Web.Script.Services.ScriptService]屬性,
並繼承System.Web.Services.WebService
方法不可宣告static否則無效aspx頁面的AutoCompleteExtender擴充項
須指定ServicePath
不使用WebService加入一個類別AutoComplete_WebPage.cs類別繼承System.Web.UI.Page一定要宣告成static才有效果aspx.cs須繼承自訂類別
不使用WebService做法的原理:
每支aspx程式要有自己的public static Method(服務方法),若寫在MasterPage或WebUserControl的話,ServiceMethod會抓不到,所以只好用繼承的方式,讓有需要做自動完成功能的aspx程式繼承自訂類別,而該自訂類別就是實現自動完成功能的System.Web.UI.Page子類別

2011/9/2 眼尖的讀者可以發現以上的SQL查詢會有SQL Injection危險,所以以上 SqlDataAdapter 的查詢語法最好改成以下:
string selectStr = @"SELECT Top (" + count + ") CompanyName FROM Customers Where CompanyName Like  @prefixText + '%' Order by CustomerID ASC";
SqlDataAdapter da = new SqlDataAdapter(selectStr, conn);
da.SelectCommand.Parameters.AddWithValue("@prefixText", prefixText);
再附上黑暗執行緒網友的jQuery AutoComplete懶人包文章
2011/10/19 追加說明
如果是在MasterPage或是WebControl做自動完成的話,建議採用WebService的方式,才能成功。