同事問到,如何以 Ajax 的方式取得 UserControl 的內容。
問題
由於 UserControl 是無法單獨被客戶端所呼叫(Request),因此我們必須想個辦法將 UserControl 加到一個 Page內。
解決步驟
1 Web Service 回傳 UserControl 的內容
使用 Visual Studio (我使用 2010) 建立一個 Web Application,並加入一個 WebService。程式碼如下
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class WebService1 : System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
public string GetUserControlHtml(string location)
{
var webServiceString = string.Format("Thread Id={0}, Time={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HHmmss fffffff"));
var page = new Page();
var userControl = (UserControl)page.LoadControl(location);
userControl.EnableViewState = false;
var form = new HtmlForm();
form.Controls.Add(userControl);
page.Controls.Add(form);
var writer = new StringWriter();
HttpContext.Current.Server.Execute(page, writer, false);
return webServiceString + "<br /> " + RemoveFormTagFromHtml(writer.ToString());
}
/// <summary>
/// 移掉 Form Tag
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
private static string RemoveFormTagFromHtml(string html)
{
return Regex.Replace(html, @"<[/]?(form)[^>]*?>", "", RegexOptions.IgnoreCase);
}
}
過程大略是:手動建立一個 Page,Page 內加入一個 Form,Form 動態載入指定的 UserControl。使用 Server.Execute 取得 html 內容後,將 <form tag 移除,避免避免原 asp.net 網頁失敗。
2 寫 UserControl
今天的配角是 UserControl。UserControl並不難實作。我簡單的寫了兩個 UserControl,分別 Thread.Sleep 了2秒,3秒。
3 於呼叫網頁使用 Ajax 方式呼叫 ASP.NET Web Service
我再入成兩種方式來呼叫。一個是 ASP.NET AJAX,一個則是 jQuery。
3.1 使用 ASP.NET AJAX 呼叫 Web Service
加入一個 ScriptManager,並加入一個 ScriptReference 參考到剛剛建立的 WebService。利用動態產生的 javascript file (可使用 http://…/WebService1.asmx/jsdebug 取得內容)協助呼叫 ASP.NET Web Service
3.2 使用jQuery 的 .ajax 呼叫 Web Service
使用jQuery 大概是近來 Web 技術的顯學吧。為了方便未來的 ASP.NET MVC的使用,還是乖乖的學一下吧。
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="WebApplication3._Default" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
var datestring = getNow();
$("#div1").html("at client, " + datestring + "<br/>");
getData("WebUserControl1.ascx", $("#div1"));
datestring = getNow();
$("#div2").html("at client, " + datestring + "<br/>");
getDataByJquery("WebUserControl2.ascx", $("#div2"));
});
function getNow() {
var today = new Date();
return today.format("HHmmss ffffff");
}
function getData(controlLocation, container) {
WebApplication3.WebService1.GetUserControlHtml(controlLocation,
function (result) {
$(container).append(result);
},
function () { alert(error.get_message()); }
);
}
function getDataByJquery(controlLocation, container) {
var datestring = getNow();
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "WebService1.asmx/GetUserControlHtml",
data: "{'location': '" + controlLocation + "'}",
dataType: "json",
success: function (result) {
$(container).append(result.d);
},
error: function (error) { alert(error.responseText); }
});
}
</script>
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/WebService1.asmx" />
</Services>
</asp:ScriptManager>
<h2>
Welcome to ASP.NET!
</h2>
<div style="border: 2px solid red">
<h1>
User Controll 1</h1>
<div id="div1">
</div>
</div>
<div style="border: 2px solid red">
<h1>
User Controll 2</h1>
<div id="div2">
</div>
</div>
<div id="result">
</div>
</asp:Content>
4 測試
測試時發現狀況還不錯。雖然同時發出 Request, 約2秒後得到UserControl1 的內容,但 UserControl2卻在約略在 5.5 秒後才得到內容,而非3秒。為什麼?
5 Session 造成 lock,因而 Delay
原來是步驟1中,[WebMethod(EnableSession = true)] 這個 true 所造成的。改成 EnableSessioin = false後,網頁測試結果如下
這樣就符合我們的期待了。
沒事最好還是不要用 Session。
範例下載