决战.NET
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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的代码。

true

图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所示的画面。

true

图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控件的出错信息。