65.9K
CodeProject 正在变化。 阅读更多。
Home

漂亮的 Prototype 自动完成,易于使用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.36/5 (6投票s)

2011年2月1日

CPOL

3分钟阅读

viewsIcon

35368

downloadIcon

724

一个美观且易于使用的 ASP.NET Ajax Web控件

引言

我热爱 Web 创建,并且热爱美观的 Web 图形界面。上次,我搜索了一个美观的 ASP.NET 自动建议 Web 控件,它能够每项内容写成两行。

我做了一些研究,找到了这个优雅的控件

该控件的编辑提供了 JavaScript 库文件和一个包含 PHP 示例的 CSS 文件。

我决定将其制作成一个 ASP.NET Web 控件,并且我尝试将其设计得像 Microsoft Widgets 一样简单易用。

此控件与所有浏览器兼容。

背景

此控件需要 3 个基本要素才能运行

  1. 您必须提供 ServiceMethod 属性,它可以是 .asmx 文件或 .svc 文件
  2. 您必须提供 ServicePath 属性,它是您 webservicewcfservice 中方法的名称,用于调用以获取格式化数据并绑定到控件。
  3. 在此服务方法实现中,您必须遵守 JSON 格式的结果。 string 必须按如下方式格式化
    { results: [" 
    "{ id: '1', value: 'TOTO,TETE', info: 'FireFox' }," 
    "{ id: '2', value: 'TATA,TYTY', info: 'Chrome' }," 
    "{ id: '3', value: 'TITI,TNTN', info: 'Safari' } ] }" 

自动建议功能使用 'value' 属性。

Using the Code

此控件是一个非常简单的 CompositeControl。 它是一个 Textbox 用于建议,并带有一个关联的 HTML 隐藏输入来存储当前选定的 ID。

OnPreRender 方法执行以下几件事

它将 JavaScript 和 CSS 文件的引用添加到文档中。

有时,CSS 文件会引用嵌入式图片。 我提取了 CSS 文件的这段代码,并将其放在服务器端。

我已经在我另一篇文章 ASP.NET Combobox dropdownlist with Images 中解释了这种获取程序集中图片地址的操作。

最后,它会加载一个 JavaScript 函数,该函数将文本框绑定到服务,以便在按下某个键并且焦点位于文本框中时进行调用。

protected override void OnPreRender(EventArgs e)
{
	base.OnPreRender(e);

	if (!this.DesignMode)
	{
	// Test for ScriptManager and register if it exists
	// we need it for PageRequestManager manipulation in prerender
	ScriptManager sm = ScriptManager.GetCurrent(Page);

	if (sm == null)
		throw new HttpException
		("A ScriptManager control must exist on the current page.");

	//Add js and css files resource
	string cssdd = Page.ClientScript.GetWebResourceUrl
		(GetType(), "ClassLibrary1.style.styledautosuggest.css");
	string scriptJQuery = Page.ClientScript.GetWebResourceUrl(this.GetType(), 
				"ClassLibrary1.script.bsn.AutoSuggest_2.1.3.js");

	HtmlLink lnk = new HtmlLink();
	lnk.Href = cssdd;
	lnk.Attributes["rel"] = "stylesheet";
	lnk.Attributes["type"] = "text/css";

	HtmlGenericControl jq = new HtmlGenericControl("script");
	jq.Attributes.Add("language", "JavaScript");
	jq.Attributes.Add("type", "text/javascript");
	jq.Attributes.Add("src", scriptJQuery);
	jq.Attributes.Add("alt", "bsn.AutoSuggest_2.1.3.js");

	//Put one time the style whose gives reference to arrow of the combo
	if (!Page.Items.Contains("styleAlreadyPut"))
	{
	//get URL of embedded pictures
	string urlImage_as_pointer_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.as_pointer.gif");
	string urlImage_as_pointer_png = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.as_pointer.png");
	string urlImage_hl_corner_bl_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.hl_corner_bl.gif");
	string urlImage_hl_corner_br_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.hl_corner_br.gif");
	string urlImage_hl_corner_tl_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.hl_corner_tl.gif");
	string urlImage_hl_corner_tr_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.hl_corner_tr.gif");
	string urlImage_li_corner_png = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.li_corner.png");
	string urlImage_ul_corner_png = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.ul_corner.png");
	string urlImage_ul_corner_bl_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.ul_corner_bl.gif");
	string urlImage_ul_corner_br_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.ul_corner_br.gif");
	string urlImage_ul_corner_tl_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.ul_corner_tl.gif");
	string urlImage_ul_corner_tr_gif = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "ClassLibrary1.img.ul_corner_tr.gif");
   
	//--------------------------------------------------------
	StringBuilder strStyleas_pointer_gif = new StringBuilder();
	strStyleas_pointer_gif.Append(" div.autosuggest { ")
	   .Append(" position: absolute; ")
	   .Append(" background-position: top; ")
	   .Append(" background-repeat: no-repeat; ")
	   .Append(" padding: 10px 0 0 0; ")
	   .Append(" background-image:url('" + urlImage_as_pointer_gif + "');")
   .Append(" } ");
	HtmlGenericControl styleas_pointer_gif = new HtmlGenericControl("style");
	styleas_pointer_gif.Attributes.Add("type", "text/css");
	styleas_pointer_gif.Controls.Add(new LiteralControl
			( strStyleas_pointer_gif.ToString()));
	Page.Header.Controls.Add(styleas_pointer_gif);
	...
	...
	...
	...
	...
    }	
	StringBuilder autoSuggestParam = new StringBuilder();
	string ServiceUrl = ServicePath + "/" + ServiceMethod + "?";

	autoSuggestParam.Append(" var options = { ")
		 .Append("script:\"" + ServiceUrl + "\", ")
		 .Append("varname:\"input\",")
		 .Append("json:true,")
		 .Append("shownoresults:false,")
		 .Append("maxresults:10000,")
		 .Append("callback: function (obj) 
			{ document.getElementById('" + 
			hiddenValue.ClientID + "').value = obj.id; }};");
	autoSuggestParam.Append(" var as_json = new bsn.AutoSuggest
			('" + txtAutoSuggest.ClientID + "', options); ");

	//add the control
	LiteralControl lc = new LiteralControl
		("<script language="javascript">" + autoSuggestParam + "</script>");
	this.Controls.Add(lc);

现在,详细了解 bsn.AutoSuggest_2.1.3.js JavaScript 文件会很有趣。

当我下载 bsn.AutoSuggest 文件时,var 选项(就在上面)的 'script' 属性是 script:"test.php?id=.."

脚本值 (test.php?id=..) 最终允许调用 XMLHttpRequest 并异步调用页面 test.php,并带有一些 GET 参数来绑定数据

_b.Ajax.prototype.makeRequest = function(url, meth, onComp, onErr) {

    if (meth != "POST")
        meth = "GET";

    this.onComplete = onComp;
    this.onError = onErr;

    var pointer = this;

    // branch for native XMLHttpRequest object
    if (window.XMLHttpRequest) {

        this.req = new XMLHttpRequest();
        this.req.onreadystatechange = function() { pointer.processReqChange() };
        this.req.open("GET", url, true); //
        this.req.send(null);
        // branch for IE/Windows ActiveX version
    }
    else if (window.ActiveXObject) {

        this.req = new ActiveXObject("Microsoft.XMLHTTP");
        if (this.req) {
            this.req.onreadystatechange = function() { pointer.processReqChange() };
            this.req.open(meth, url, true);
            this.req.send();
        }
    }
};

因此,我对自己说,我必须尝试保持相同的机制来调用 Web 服务和 WCF 服务。

我了解到可以使用 XMLHttpRequest 通过 GET 参数调用 Web 服务。

脚本值必须按如下方式格式化:ServicePath + "/" + ServiceMethod + "?param=p" 在这里非常简单:Webservice.asmx/getResults?input=...

要授权此调用,您必须在 Web.config 中添加一个部分

    <webservices>
      <protocols>
        <add name="HttpGet">
        <add name="HttpPost">
      </add>
    </add>
</protocols></webservices>

要调用 WCF 服务,机制完全相同:Service.svc/getResults?input=...

有趣的是,要从 Ajax 请求调用 WCF 合同方法,该方法必须具有 [WebGet()] 属性,并且 WCF 服务类必须具有 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 属性。

类似地,要从 Ajax 请求调用 Web 服务,该方法必须具有 [ScriptMethod] 属性,并且 Web 服务类必须具有 [ScriptService] 属性。

最后一点,当我们收到 Web 服务的响应时,它是一个带有 XML 的包装响应,因此我们必须剥离 XML。

WCF 更好,因为它发送干净的响应... :)

_b.AutoSuggest.prototype.setSuggestions = function(req, input) {

    // if field input no longer matches what was passed to the request
    // don't show the suggestions
    //
    if (input != this.fld.value)
        return false;

    this.aSug = [];

    if (this.oP.json) {

        var txtResponse = req.responseText;
        var jsondata = '';

        // web service case
        if (txtResponse.charAt(0) == '<') {
        
            /*
            <string xmlns="http://tempuri.org/">
                { results: [{ id: '1', value: 'Foobar', info: 'Cheshire' },
                            { id: '2', value: 'Foobarfly', info: 'Shropshire' },
                            { id: '3', value: 'Foobarnacle', info: 'Essex' } ] }
            </string>*/
            //STRIP XML and HTML tags ( tratment)    
            var matchTag = /<(?:.|\s)*?>/g;
            txtResponse = txtResponse.replace(matchTag, "");
            jsondata = eval('(' + txtResponse + ')');
        }
        //WCF service case
        else 
        {
            /*{"d":"   { results: 
            [{ id: \"1\", value: \"Foobar\", info: \"Cheshire\" },
            { id: \"2\", value: \"Foobarfly\", info: \"Shropshire\" },
            { id: \"3\", value: \"Foobarnacle\", info: \"Essex\" } ] }    "}*/

            var alphaChars = /^([a-zA-Z])$/;

            if (alphaChars.test(txtResponse.charAt(2))) 
            {
                txtResponse = txtResponse.replace("{\"d\":\"", "");
                txtResponse = txtResponse.substring(0, txtResponse.length - 2);
                jsondata = eval('(' + txtResponse + ')');
            }
        }

        for (var i = 0; i < jsondata.results.length; i++) {
            this.aSug.push({ 'id': jsondata.results[i].id, 
	   'value': jsondata.results[i].value, 'info': jsondata.results[i].info });
        }
    }
    else {
        var xml = req.responseXML;

        // traverse xml
        //
        var results = xml.getElementsByTagName('results')[0].childNodes;

        for (var i = 0; i < results.length; i++) {
            if (results[i].hasChildNodes())
                this.aSug.push({ 'id': results[i].getAttribute('id'), 
		'value': results[i].childNodes[0].nodeValue, 
		'info': results[i].getAttribute('info') });
        }    }

    this.idAs = "as_" + this.fld.id;

    this.createList(this.aSug);
};

关注点

了解如何使用基本的 JavaScript 对象 XMLHttpRequest 调用 .NET 服务非常有趣。

一旦你看到了这些,你就可以大致了解 ajaxtoolkit 如何使用它的 ScriptManager 工作。

历史

  • 2011 年 2 月 1 日:初始发布
© . All rights reserved.