有時候想做簡單的Ajax顯示Readonly資料,但自己Parse Html code也太麻煩又不好用.既保留VS的設計介面和功能,又能輸出成Html的方法.
這功能,在有UpdatePanel和Web Service之後,好像沒有甚麼存在價值,但我就是遇到了,很巧合地Rick Strahl和Scott Guthrie也遇到這樣的問題,在一輪試驗之後,來這裡分享一下成果.
這個功能的目標是這樣的,用一個AJAX Call去取得HTML,再呈現在介面上,不包含任何控制,也就是AJAX的最基本,Rick Strahl的解法更進一步去做到Postback Controls,而且要用到他的library,所以我在這裡是用Scott Guthrie的方法.
首先,把你想要呈現的資料片段,設計一個使用者自定元件(.asmx),裡面可以放任何元任, Label, GridView等都可以,還可以設定GridView的DataBind.設計好之後要動點小手腳,在code-behind裡把要傳到這個元件的資料,例如GridView的DataSource,定義一個public變數,例如
public partial class MyUserControl : System.Web.UI.UserControl
{
public object Data;
public string LabelText;
protected void Page_Load(object sender, EventArgs e)
{
lbl_Name.Text = LabelText;
gdv_data.DataSource = Data;
gdv_data.DataBind();
}
}然後就要建立一個方法來服務AJAX Call,可以是Web Service, 頁面, 泛型處理常式等,我這裡是用處理常式.
在.ashx裡面,實作處理需求的方法
//return the html of user control
public void ProcessRequest(HttpContext context)
{
string l_response;
//get your data source data to l_datasource
if (l_datasource.Length > 0)
{
//extend from Page Class
RenderPage l_page = new RenderPage();
//pass data to user control as a dictionary
Dictionary<string, object> l_data = new Dictionary<string, object>(2);
l_data.Add("Data", l_datasource);
l_data.Add("LabelText", "the name");
//Method that load user control and pass data to it
l_response = ViewManager.RenderView(@"~/WebService/MonitorInfoWindow.ascx", l_data);
}
else
{
l_response = "No Data";
}
context.Response.ContentType = "text/plain";
//write out the rendered control HTML text
context.Response.Write(l_response);
} 把你要傳進Usre Control的資料物件存到一個Dictionary裡面,Key是在User Control裡對應的變數名稱.
然後用RenderView()這個方法來取得Render後的字串,方法實作如下
public static string RenderView(string path, Dictionary<string,object> data)
{
RenderPage pageHolder = new RenderPage();
UserControl viewControl = (UserControl)pageHolder.LoadControl(path);
//should use a dictionary to set data to multiple fields
if (data != null)
{
foreach (KeyValuePair<string, object> pair in data)
{
Type viewControlType = viewControl.GetType();
FieldInfo field = viewControlType.GetField(pair.Key);
if (field != null)
{
field.SetValue(viewControl, pair.Value);
}
else
{
throw new Exception(string.Format("The Control {0} do not have a public {1} property", path, pair.Key));
}
}
}
pageHolder.Controls.Add(viewControl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}其實是用reflection把傳進來的值設定好,再用Server.Execute去執行Page物件,這樣就會像真實的頁面一樣
去做Init, Load, Binding等動作,最後輸出成元作的HTML.
這裡有一個地方要注意,就是為甚麼要用繼承的Page而不用原本的Page.
因為Page物件會去檢查元件是否在伺服器執行,用這種虛擬讀進來的Control在檢查時會失敗
所以我們覆寫那個檢查方法來跳過檢查.
public class RenderPage : Page
{
public override void VerifyRenderingInServerForm(Control control)
{
return;
}
}這樣就大功告成了,記得這方法只適合用在Readonly的元作,如果要實作有Postback功能的請參考Rick的文章.