- 決戰.NET
- 黃忠成
- 1666字
- 2018-12-27 16:56:04
5.2 當UpdatePanel遇上MasterPage
MasterPage技術的出現,讓設計師可以輕松地設計網頁樣板,ASP.NET AJAX自然也支持這種架構,設計師只需在MasterPage中放置一個ScriptManager控件,之后套用此MasterPage的頁面便可直接放入UpdatePanel等ASP.NET AJAX的控件,無需再放入ScriptManager控件。這樣的應用相當常見,并無任何可議之處,唯一可能會引發問題的情況是套用MasterPage之頁面內的UpdatePanel控件需要依賴MasterPage中的控件來刷新時,此時并無法依賴Triggers來實現,因為你無法在Triggers編輯窗中選取位于MasterPage中的控件,這有兩種解法,一是將MasterPage中欲觸發UpdatePanel刷新的控件,通過ScriptManager的RegisterAsyncPostback函數注冊成Async-Postback的控件,一旦完成此動作后,該控件的任何Postback動作將轉變成Async-Postback,再對應于事件中,以Update函數來更新UpdatePanel即可。請照著下列步驟做。
1. 于AdvAjaxDemo的Web Site項目中新增一個MasterPage,命名為Default.master。
2. 在其內鍵入程序5-3的程序代碼。
3. 在Default.master.cs中鍵入程序5-4的代碼。
程序5-3
Samples\5\AdvAjaxDemo\Default.master <%@ Master Language="C#" AutoEventWireup="true" CodeFile="Default.master.cs" Inherits="_Default" %> <!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 runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <table> <tr> <td valign=top> <asp:TreeView ID="TreeView1" runat="server" ImageSet="Msdn" NodeIndent="10" OnSelectedNodeChanged="TreeView1_SelectedNodeChanged"> <ParentNodeStyle Font-Bold="False" /> <HoverNodeStyle BackColor="#CCCCCC" BorderColor="#888888" BorderStyle="Solid" Font-Underline="True" /> <SelectedNodeStyle BackColor="White" BorderColor="#888888" BorderStyle="Solid" BorderWidth="1px" Font-Underline="False" HorizontalPadding="3px" VerticalPadding="1px" /> <NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black" HorizontalPadding="5px" NodeSpacing="1px" VerticalPadding="2px" /> </asp:TreeView> </td> <td valign=top> <asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </td> </tr> </table> </div> </form> </body> </html>
程序5-4
Samples\5\AdvAjaxDemo\Default.master.cs using System; using System.Data; using System.Data.SqlClient; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.MasterPage { private void BindTreeView() { TreeView1.Nodes.Clear(); TreeNode currentMasterNode = null; using (SqlConnection conn = new SqlConnection( ConfigurationManager.ConnectionStrings[ "NorthwindConnectionString"].ConnectionString)) { using(SqlCommand cmd = new SqlCommand( "SELECT City FROM Customers GROUP BY City ORDER BY City",conn)) { conn.Open(); using(SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { while(reader.Read()) { if (!reader.IsDBNull(0)) { string city = reader.GetString(0); if (currentMasterNode == null) { currentMasterNode = new TreeNode( city.Substring(0, 1), "NonSelect"); TreeView1.Nodes.Add(currentMasterNode); } else { if (currentMasterNode.Text != city.Substring(0, 1)) { currentMasterNode = new TreeNode(city. Substring(0, 1), "NonSelect"); TreeView1.Nodes.Add(currentMasterNode); } } currentMasterNode.ChildNodes.Add(new TreeNode(city)); } } } } } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostback && !ScriptManager1.IsInAsyncPostback) BindTreeView(); ScriptManager1.RegisterAsyncPostbackControl(TreeView1); } protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e) { UpdatePanel up = ContentPlaceHolder1.FindControl("UpdatePanel1") as UpdatePanel; up.Update(); } }
此MasterPage一開始時,會由Northwind數據庫中的Customers數據表以GROUP BY City的方式取出城市數據列表,然后一一指定給頁面上的TreeView1控件,接著在Page_Load函數中將TreeView1注冊為Async-Postback控件,這時TreeView1的任何Postback動作都會被轉換成Async-Postback,不會引發整個頁面的刷新,接著在TreeView1的SelectedNodeChanged事件中,通過ContentPlaceHolder1取得要刷新的UpdatePanel,調用Update函數要求刷新。完成后請再照著下列步驟建立套用此MasterPage的頁面。
1. 新增一個網頁,命名為TreeViewWithGridView.aspx,套用Default.master作為Master Page。
2. 在頁面中放入一個UpdatePanel控件,命名為UpdatePanel1。
3. 在UpdatePanel1控件中放入一個SqlDataSource控件,命名為SqlDataSource1,連結至Northwind數據庫的Customers數據表,選取CustomerID、CompanyName、ContactTitle、ContactName字段,指定一個WHERE參數,如圖5-5所示。
4. 在UpdatePanel1控件中放入一個GridView控件,命名為GridView1,DataSourceID設為SqlDataSource1,勾選Enable Paging。
5. 在Page_Load函數中鍵入程序5-5的代碼。

圖5-5
程序5-5
Samples\5\AdvAjaxDemo\ TreeViewWithGridView.aspx.cs protected void Page_Load(object sender, EventArgs e) { if (ScriptManager.GetCurrent(this).IsInAsyncPostback && ScriptManager.GetCurrent(this).AsyncPostbackSourceElementID. EndsWith("TreeView1")) { TreeView tv = (TreeView)Master.FindControl(ScriptManager.GetCurrent(this). AsyncPostbackSourceElementID); if (tv.SelectedValue != "NonSelect") { SqlDataSource1.SelectParameters["City"].DefaultValue = tv.SelectedValue; GridView1.DataBind(); } } }
完成后將此頁面設為默認并運行,便可看到如圖5-6所示的畫面。

圖5-6
眼尖的讀者應發現這個設計有些問題,MasterPage與套用MasterPage的頁面有著一小層鏈接,那就是套用MasterPage的頁面之UpdatePanel控件必須命名為UpdatePanel1,這在大量使用MasterPage技術的系統中,會顯得非常不恰當,若能將此鏈接切斷,改由套用MasterPage的頁面來決定哪些UpdatePanel控件要隨著MasterPage上的控件動作而更新,豈不更好?辦得到嗎?當然!只要將MasterPage中的TreeView1設為UpdatePanel的Trigger就可以了,見程序5-6。
程序5-6
Samples\5\AdvAjaxDemo\ TreeViewWithGridView.aspx.cs using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class TreeViewWithGridView : System.Web.UI.Page { private void AttachUpdateTrigger() { TreeView tv = (TreeView)Master.FindControl("TreeView1"); AsyncPostbackTrigger trigger = new AsyncPostbackTrigger(); trigger.ControlID = tv.ID; trigger.EventName = "SelectedNodeChanged"; UpdatePanel1.Triggers.Add(trigger); } protected void Page_Init(object sender, EventArgs e) { AttachUpdateTrigger(); } protected void Page_Load(object sender, EventArgs e) { if (ScriptManager.GetCurrent(this).IsInAsyncPostback && ScriptManager.GetCurrent(this).AsyncPostbackSourceElementID. EndsWith("TreeView1")) { TreeView tv = (TreeView)Master.FindControl( ScriptManager.GetCurrent(this).AsyncPostbackSourceElementID); if (tv.SelectedValue != "NonSelect") { SqlDataSource1.SelectParameters["City"].DefaultValue = tv.SelectedValue; GridView1.DataBind(); } } } }
程序5-6在Page_Init時調用了AttachUpdateTrigger函數,此函數會利用Master屬性來找到位于MasterPage中的TreeView1控件,并將其添加成UpdatePanel1的Trigger,此舉意味著當TreeView1發生Postback時,將會被轉變為Async-Postback,并引發UpdatePanel1的刷新動作。一旦此頁面添加Triggers后,MasterPage頁面就不需要再使用RegisterAsyncPostback來將TreeView1注冊為Async-Postback控件,也不需于TreeView1的SelectedNodeChanged事件中以Update函數來刷新UpdatePanel了,一切都移往套用MasterPage技術的頁面,這不是更符合群體開發時的情況嗎?程序5-7是修改后的Default.master.cs。
程序5-7
Samples\5\AdvAjaxDemo\Default.master.cs using System; using System.Data; using System.Data.SqlClient; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.MasterPage { private void BindTreeView() { TreeView1.Nodes.Clear(); TreeNode currentMasterNode = null; using (SqlConnection conn = new SqlConnection( ConfigurationManager.ConnectionStrings[ "NorthwindConnectionString"].ConnectionString)) { using(SqlCommand cmd = new SqlCommand( "SELECT City FROM Customers GROUP BY City ORDER BY City",conn)) { conn.Open(); using(SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { while(reader.Read()) { if (!reader.IsDBNull(0)) { string city = reader.GetString(0); if (currentMasterNode == null) { currentMasterNode = new TreeNode( city.Substring(0, 1), "NonSelect"); TreeView1.Nodes.Add(currentMasterNode); } else { if (currentMasterNode.Text != city.Substring(0, 1)) { currentMasterNode = new TreeNode(city.Substring(0,1),"NonSelect"); TreeView1.Nodes.Add(currentMasterNode); } } currentMasterNode.ChildNodes.Add(new TreeNode(city)); } } } } } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostback && !ScriptManager1.IsInAsyncPostback) BindTreeView(); // ScriptManager1.RegisterAsyncPostbackControl(TreeView1); } protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e) { //UpdatePanel up = ContentPlaceHolder1.FindControl("UpdatePanel1") as UpdatePanel; //up.Update(); } }
ScriptManagerProxy與MasterPage
將ScriptManager控件放在MasterPage上的缺點是,套用此MasterPage的網頁由于沒有ScriptManager控件,所以無法通過ScriptManager控件的Scripts、Services來引入外部的JavaScript程序文件及Web Services,這時另一個ScriptManagerProxy控件便派上用場了,只要將ScriptManagerProxy控件放在套用MasterPage的頁面內的ContentPlaceHolder中,設計師便能通過它所提供的Scripts、Services來設定要引入的JavaScript程序文件及Web Services,這些設定會于此頁面被瀏覽時迭加到MasterPage中的ScriptManager控件內。舉個例子來說,Default2.master中的ScriptManager之Scripts引用了JScript1.js,Default2.aspx是套用了Default2.master作為MasterPage的頁面,只要在Default2.aspx的ContentPlaceHolder中放入一個ScriptManagerProxy控件,于其Scripts中添加引用JScript2.js,那么當用戶瀏覽Default2.aspx時,該頁面就會引用JScript1.js、JScript2.js兩個JavaScript程序文件。那如果MasterPage頁面中沒有ScriptManager控件,而套用MasterPage的頁面有呢?結果并不意外,ScriptManagerProxy控件會丟出需要ScriptManager控件的出錯信息。