从母版页与内容页交互 (C#)

作者 :Scott Mitchell

检查如何从母版页中的代码调用内容页的方法、设置属性等。

简介

前面的教程介绍了如何让内容页以编程方式与其母版页交互。 回想一下,我们更新了母版页,以包含一个 GridView 控件,该控件列出了最近添加的五种产品。 然后,我们创建了一个内容页面,用户可以从中添加新产品。 添加新产品后,内容页需要指示母版页刷新其 GridView,以便包含刚添加的产品。 此功能是通过向母版页添加公共方法来实现的,该方法刷新绑定到 GridView 的数据,然后从内容页调用该方法。

最常见的内容和母版页交互形式源自内容页。 但是,母版页可能会使当前内容页生效,如果母版页包含允许用户修改也在内容页上显示的数据的用户界面元素,则可能需要此类功能。 请考虑在 GridView 控件中显示产品信息的内容页,以及包含按钮控件的母版页,单击该按钮时,所有产品的价格翻倍。 与上一教程中的示例非常类似,需要在单击双价按钮后刷新 GridView,以便显示新价格,但在此方案中,母版页需要将内容页加入操作。

本教程探讨如何在内容页中定义母版页调用功能。

通过事件和事件处理程序启动编程交互

从母版页调用内容页功能比另一种方式更具挑战性。 由于内容页具有单个母版页,因此在从内容页发起编程交互时,我们知道有哪些公共方法和属性可供我们使用。 但是,母版页可以具有许多不同的内容页,每个页面都有自己的一组属性和方法。 那么,当我们不知道在运行时之前将调用哪些内容页时,如何在母版页中编写代码以在其内容页中执行某些操作?

请考虑 ASP.NET Web 控件,例如 Button 控件。 Button 控件可以出现在任意数量的 ASP.NET 页上,并且需要一种机制,通过该控件可以提醒页面已单击它。 这是使用 事件完成的。 具体而言,Button 控件在单击时引发其 Click 事件;包含 Button 的 ASP.NET 页可以选择性地通过 事件处理程序响应该通知。

此相同模式可用于在其内容页中具有母版页触发器功能:

  1. 将事件添加到母版页。
  2. 每当母版页需要与其内容页通信时引发 事件。 例如,如果母版页需要提醒其内容页用户已将价格翻倍,则会在价格翻倍后立即引发其事件。
  3. 在需要执行某些操作的内容页中创建事件处理程序。

本教程的余下部分将实现简介中概述的示例;即列出数据库中产品的内容页,以及包含 Button 控件的母版页,用于将价格加倍。

步骤 1:在内容页中显示产品

我们的第一个业务顺序是创建一个内容页,其中列出了 Northwind 数据库中的产品。 (我们在前面的教程“ 与内容页中的母版页交互”中向项目添加了 Northwind 数据库。) 首先将一个新的 ASP.NET 页添加到 ~/Admin 名为 Products.aspx的文件夹中,确保将其绑定到 Site.master 母版页。 图 1 显示了将此页面添加到网站后解决方案资源管理器。

向管理员文件夹添加新 ASP.NET 页

图 01:向文件夹添加新 ASP.NET 页 Admin (单击以查看全尺寸图像)

回想一下,在 母版页中指定标题、元标记和其他 HTML 标头教程中 ,我们创建了一个名为 BasePage 的自定义基页类,如果未显式设置,该类将生成页面的标题。 转到 Products.aspx 页面的代码隐藏类,使其派生自 BasePage (而不是 System.Web.UI.Page) 。

最后,更新 文件以 Web.sitemap 包含本课程的条目。 为内容到母版页交互课程的 下面 <siteMapNode> 添加以下标记:

<siteMapNode url="~/Admin/Products.aspx" title="Master to Content Page Interaction" />

<siteMapNode> 元素的添加反映在课程列表 (见图 5) 。

返回到 Products.aspx。 在 的 MainContentContent 控件中,添加 GridView 控件并将其命名为 ProductsGrid。 将 GridView 绑定到名为 ProductsDataSource的新 SqlDataSource 控件。

将 GridView 绑定到新的 SqlDataSource 控件

图 02:将 GridView 绑定到新的 SqlDataSource 控件 (单击以查看全尺寸图像)

配置向导,使其使用 Northwind 数据库。 如果已完成上一教程,则应已在 中Web.config具有名为 NorthwindConnectionString 的连接字符串。 从下拉列表中选择此连接字符串,如图 3 所示。

将 SqlDataSource 配置为使用 Northwind 数据库

图 03:将 SqlDataSource 配置为使用 Northwind 数据库 (单击以查看全尺寸图像)

接下来,通过从下拉列表中选择 Products 表并返回 ProductNameUnitPrice 列来指定数据源控件的 SELECT 语句, (请参阅图 4) 。 单击“下一步”,然后单击“完成”以完成“配置数据源”向导。

返回 Products 表中的 ProductName 和 UnitPrice 字段

图 04:从ProductNameProducts表返回 和 UnitPrice 字段 (单击以查看全尺寸图像)

就是这么简单! 完成向导后,Visual Studio 将两个 BoundField 添加到 GridView,以镜像 SqlDataSource 控件返回的两个字段。 GridView 和 SqlDataSource 控件的标记如下所示。 图 5 显示了通过浏览器查看时的结果。

<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
 DataSourceID="ProductsDataSource">
 <Columns>
 <asp:BoundField DataField="ProductName" HeaderText="ProductName" 
 SortExpression="ProductName" />
 <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" 
 SortExpression="UnitPrice" />
 </Columns>
</asp:GridView>

<asp:SqlDataSource ID="ProductsDataSource" runat="server" 
 ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
 SelectCommand="SELECT [ProductName], [UnitPrice] FROM [Products]">
</asp:SqlDataSource>

每个产品及其价格都列在 GridView 中

图 05:每个产品及其价格在 GridView 中列出 (单击以查看全尺寸图像)

注意

随意清理 GridView 的外观。 一些建议包括将显示的 UnitPrice 值格式化为货币,以及使用背景色和字体来改善网格的外观。 有关在 ASP.NET 中显示和设置数据格式的详细信息,请参阅我的 使用数据教程系列

步骤 2:向母版页添加“双倍价格”按钮

下一个任务是将按钮 Web 控件添加到母版页,单击该控件时,该控件将使数据库中所有产品的价格翻倍。 Site.master打开母版页,将“工具箱”中的“按钮”拖动到Designer,将其放置在上一教程中添加的 SqlDataSource 控件下RecentProductsDataSource。 将 Button 的 ID 属性设置为 DoublePrice ,将其 Text 属性设置为“双倍产品价格”。

接下来,将 SqlDataSource 控件添加到母版页,并将其 DoublePricesDataSource命名为 。 此 SqlDataSource 将用于执行 语句, UPDATE 以将所有价格加倍。 具体而言,我们需要将其 ConnectionStringUpdateCommand 属性设置为适当的连接字符串 和 UPDATE 语句。 然后,我们需要在单击 Button 时DoublePrice调用此 SqlDataSource 控件的 Update 方法。 若要设置 ConnectionStringUpdateCommand 属性,请选择 SqlDataSource 控件,然后转到属性窗口。 属性 ConnectionString 在下拉列表中列出已存储在 中的 Web.config 连接字符串;选择 选项, NorthwindConnectionString 如图 6 所示。

将 SqlDataSource 配置为使用 NorthwindConnectionString

图 06:将 SqlDataSource 配置为使用 NorthwindConnectionString (单击以查看全尺寸图像)

若要设置 UpdateCommand 属性,请在属性窗口中找到 UpdateQuery 选项。 此属性在选中时显示带省略号的按钮;单击此按钮可显示“命令和参数编辑器”对话框,如图 7 所示。 在对话框的文本框中键入以下 UPDATE 语句:

UPDATE Products SET UnitPrice = UnitPrice * 2

执行此语句时,会将表中每条记录Products的值加倍UnitPrice

设置 SqlDataSource 的 UpdateCommand 属性

图 07:设置 SqlDataSource 的 UpdateCommand 属性 (单击以查看全尺寸图像)

设置这些属性后,Button 和 SqlDataSource 控件的声明性标记应如下所示:

<asp:Button ID="DoublePrice" runat="server" 
 Text="Double Product Prices" />

<asp:SqlDataSource ID="DoublePricesDataSource" runat="server" 
 UpdateCommand="UPDATE Products SET UnitPrice = UnitPrice * 2" 
 ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
 ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>">
</asp:SqlDataSource>

剩下的就是在单击 Button 时DoublePrice调用其 Update 方法。 ClickDoublePrice Button 创建事件处理程序并添加以下代码:

protected void DoublePrice_Click(object sender, EventArgs e)
{
    // Double the prices
    DoublePricesDataSource.Update();
}

若要测试此功能,请访问 ~/Admin/Products.aspx 我们在步骤 1 中创建的页面,然后单击“双倍产品价格”按钮。 单击该按钮会导致回发并执行 DoublePrice Button 的 Click 事件处理程序,使所有产品的价格翻倍。 然后重新呈现页面,并返回标记并在浏览器中重新显示。 但是,内容页中的 GridView 列出了与单击“双产品价格”按钮之前相同的价格。 这是因为最初加载到 GridView 中的数据的状态存储在视图状态中,因此除非另有说明,否则不会在回发时重新加载数据。 如果访问其他页面,然后返回到该页面, ~/Admin/Products.aspx 你将看到更新后的价格。

步骤 3:在价格翻倍时引发事件

由于页面中的 GridView ~/Admin/Products.aspx 不会立即反映价格翻倍,因此用户可以理解地认为他们没有单击“双产品价格”按钮,或者它不起作用。 他们可能会尝试再单击该按钮几次,一次又一次地将价格翻倍。 若要解决此问题,我们需要让内容页中的网格在加倍后立即显示新价格。

如本教程前面所述,每当用户单击 DoublePrice 按钮时,我们需要在母版页中引发事件。 事件是一个类 (事件发布者) 通知另一组其他类 (事件订阅者) 发生了一些有趣的事件的方式。 在此示例中,母版页是事件发布者;关注按钮单击时间 DoublePrice 的内容页面是订阅者。

类通过创建 事件处理程序来订阅事件,事件处理程序是一个为响应所引发的事件而执行的方法。 发布者通过定义事件委托来定义他引发 的事件。 事件委托指定事件处理程序必须接受的输入参数。 在.NET Framework中,事件委托不返回任何值,并接受两个输入参数:

  • 标识 Object事件源的 ,以及
  • 派生自 的类 System.EventArgs

传递给事件处理程序的第二个参数可以包含有关该事件的其他信息。 虽然基EventArgs类不会传递任何信息,但.NET Framework包含许多扩展EventArgs和包含其他属性的类。 例如,实例传递给响应事件的Command事件处理程序,并包含两个CommandEventArgs信息属性:CommandArgumentCommandName

注意

有关创建、引发和处理事件的详细信息,请参阅 简单英语的事件和委托事件委托

若要定义事件,请使用以下语法:

public event eventDelegate eventName;

由于我们只需要在用户单击 DoublePrice 按钮且不需要传递任何其他附加信息时向内容页发出警报,因此可以使用事件委托 EventHandler来定义事件处理程序,该事件处理程序接受作为其第二个参数的 类型的 System.EventArgs对象。 若要在母版页中创建事件,请将以下代码行添加到母版页的代码隐藏类:

public partial class Site : System.Web.UI.MasterPage
{
    public event EventHandler PricesDoubled;

    ...
}

上述代码将公共事件添加到名为 的 PricesDoubled母版页。 在价格翻倍后,我们现在需要提高这一事件。 若要引发事件,请使用以下语法:

if (eventName != null)
    eventName(sender, eventArgs);

其中 sendereventArgs 是要传递给订阅者的事件处理程序的值。

DoublePriceClick使用以下代码更新事件处理程序:

protected void DoublePrice_Click(object sender, EventArgs e)
{
    // Double the prices
    DoublePricesDataSource.Update();

    // Refresh RecentProducts
    RecentProducts.DataBind();

    // Raise the PricesDoubled event
    if (PricesDoubled != null)
    PricesDoubled(this, EventArgs.Empty);
}

与以前一样, Click 事件处理程序首先调用 DoublePricesDataSource SqlDataSource 控件的 Update 方法,使所有产品的价格翻倍。 之后,事件处理程序有两个新增项。 首先, RecentProducts 刷新 GridView 的数据。 此 GridView 已添加到上一教程中的母版页,并显示最近添加的五种产品。 我们需要刷新此网格,使其显示这五种产品的价格翻倍。 之后,将 PricesDoubled 引发 事件。 对母版页本身的 this 引用 () 作为事件源发送到事件处理程序,空 EventArgs 对象作为事件参数发送。

步骤 4:处理内容页中的事件

此时,每当单击 Button 控件时,母版页将DoublePrice引发其PricesDoubled事件。 但是,这只是一半的战斗 - 我们仍然需要处理订阅者中的事件。 这涉及到两个步骤:创建事件处理程序和添加事件连接代码,以便在引发事件时执行事件处理程序。

首先创建名为 的 Master_PricesDoubled事件处理程序。 由于我们在母版页中定义 PricesDoubled 事件的方式,事件处理程序的两个输入参数必须分别为 类型和 ObjectEventArgs。 在事件处理程序中, ProductsGrid 调用 GridView 的 DataBind 方法以将数据重新绑定到网格。

private void Master_PricesDoubled(object sender, EventArgs e)
{
    // Rebind data to ProductsGrid
    ProductsGrid.DataBind();
}

事件处理程序的代码已完成,但我们尚未将母版页的事件 PricesDoubled 连接到此事件处理程序。 订阅服务器通过以下语法将事件连接到事件处理程序:

publisher.eventName += new eventDelegate(methodName);

publisher 是对提供 eventName 的 对象的引用, methodName 是在订阅服务器中定义的事件处理程序的名称,该事件处理程序具有对应于 eventDelegate 的签名。 换句话说,如果事件委托为 EventHandler,则 methodName 必须是订阅服务器中方法的名称,该方法不返回值,并且分别接受类型 Object 为 和 EventArgs的两个输入参数。

此事件连接代码必须在第一个页面访问和后续回发时执行,并且应在可能引发事件之前的页面生命周期中的某个时间点发生。 添加事件连接代码的好时机是在 PreInit 阶段,这发生在页面生命周期的早期。

打开 ~/Admin/Products.aspx 并创建 Page_PreInit 事件处理程序:

protected void Page_PreInit(object sender, EventArgs e)
{
    // TODO: Put event wiring logic here
}

为了完成此连接代码,我们需要从内容页对母版页进行编程引用。 如上一教程中所述,可通过两种方式执行此操作:

  • 通过将松散类型 Page.Master 属性强制转换为相应的母版页类型,或者
  • 通过在页面中添加 @MasterType 指令 .aspx ,然后使用强类型 Master 属性。

让我们使用后一种方法。 将以下 @MasterType 指令添加到页面声明性标记的顶部:

<%@ MasterType VirtualPath="~/Site.master" %>

然后在事件处理程序中添加以下事件连接代码 Page_PreInit

protected void Page_PreInit(object sender, EventArgs e)
{
    // Create an event handler for the master page's PricesDoubled event
    Master.PricesDoubled += new EventHandler(Master_PricesDoubled);
}

完成此代码后,每当 DoublePrice 单击“按钮”时,内容页中的 GridView 就会刷新。

图 8 和图 9 说明了此行为。 图 8 显示了首次访问时的页面。 请注意,GridView (母版页) 左侧列中的价格值 RecentProductsProductsGrid 内容页中的 GridView () 。 图 9 显示了单击“按钮”后紧接的 DoublePrice 同一屏幕。 如你所看到的,新价格会立即反映在两个 GridView 中。

初始价格值

图 08:初始价格值 (单击以查看全尺寸图像)

Just-Doubled 价格显示在 GridViews 中

图 09:Just-Doubled 价格显示在 GridViews (单击以查看全尺寸图像)

总结

理想情况下,母版页及其内容页是完全独立的,不需要任何级别的交互。 但是,如果母版页或内容页显示可从母版页或内容页修改的数据,则可能需要让母版页在修改数据时提醒内容页 (,) 反之亦然,以便更新显示。 在前面的教程中,我们了解了如何以编程方式让内容页与其母版页交互;本教程介绍了如何让母版页启动交互。

虽然内容与母版页之间的编程交互可能源自内容或母版页,但使用的交互模式取决于来源。 差异是由于内容页具有单个母版页,但母版页可以具有许多不同的内容页。 更好的方法是让母版页引发事件,以指示已执行某些操作,而不是让母版页直接与内容页交互。 关注操作的内容页可以创建事件处理程序。

编程快乐!

深入阅读

有关本教程中讨论的主题的详细信息,请参阅以下资源:

关于作者

Scott Mitchell 是多本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 3.5。 可以在 上联系 mitchell@4GuysFromRolla.com 斯科特,也可以通过他的博客在 联系 http://ScottOnWriting.NET

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的首席审阅者是 Suchi Banerjee。 有兴趣查看我即将发布的 MSDN 文章? 如果是这样,请在 mitchell@4GuysFromRolla.com