从母版页与内容页交互 (C#)
作者 :斯科特·米切尔
检查如何从母版页中的代码调用方法、设置属性等。
简介
前面的教程介绍了如何以编程方式让内容页与其母版页进行交互。 回想一下,我们更新了母版页,以包含列出最近添加的五个产品的 GridView 控件。 然后,我们创建了一个内容页,用户可以从中添加新产品。 添加新产品时,需要指示母版页刷新其 GridView 的内容页,以便包含刚刚添加的产品。 通过将公共方法添加到刷新绑定到 GridView 的数据的母版页,然后从内容页调用该方法来实现此功能。
最常见的内容和母版页交互形式源自内容页。 但是,母版页可能会使当前内容页生效,并且如果母版页包含用户界面元素,则可能需要此功能,使用户能够修改内容页上也显示的数据。 请考虑一个内容页,该页面显示 GridView 控件中的产品信息,以及包含按钮控件的母版页,单击后,将所有产品的价格翻倍。 与上一教程中的示例类似,在单击双价按钮后,需要刷新 GridView,以便显示新价格,但在此方案中,需要将内容页引入操作的母版页。
本教程介绍如何在内容页中定义母版页调用功能。
通过事件和事件处理程序煽动编程交互
从母版页调用内容页面功能比另一种方法更具挑战性。 由于内容页具有单个母版页,因此在从内容页中煽动编程交互时,我们知道我们掌握了哪些公共方法和属性。 但是,母版页可以具有许多不同的内容页,每个页面都有其自己的一组属性和方法。 那么,在母版页中如何编写代码,当我们不知道在运行时之前将调用什么内容页时,如何在其内容页中执行一些操作?
请考虑 ASP.NET Web 控件,例如 Button 控件。 按钮控件可以出现在任意数量的 ASP.NET 页上,并且需要一种机制,通过该机制可以提醒页面已单击该控件。 这是使用 事件完成的。 具体而言,按钮控件在单击时引发其Click
事件;包含 Button 的 ASP.NET 页可以通过事件处理程序选择性地响应该通知。
同一模式可用于在其内容页中具有母版页触发器功能:
- 将事件添加到母版页。
- 每当母版页需要与其内容页通信时引发事件。 例如,如果母版页需要提醒其内容页面用户已将价格翻倍,则其事件会在价格翻倍后立即提高。
- 在这些需要执行某些操作的内容页中创建事件处理程序。
本教程的余下部分实现了简介中概述的示例;也就是说,列出数据库中的产品的内容页和包含按钮控件的母版页,用于将价格加倍。
步骤 1:在内容页中显示产品
我们的第一个业务顺序是创建一个内容页,其中列出了 Northwind 数据库中的产品。 (我们在前面的教程中向项目添加了 Northwind 数据库, 从内容页与母版页交互。首先,将新的 ASP.NET 页添加到 ~/Admin
名为 Products.aspx
的文件夹,确保将其 Site.master
绑定到母版页。 图 1 显示此页面添加到网站后解决方案资源管理器。
图 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>
元素的添加反映在 Lessons 列表中(请参阅图 5)。
返回至 Products.aspx
. 在 Content 控件 MainContent
中,添加 GridView 控件并将其命名 ProductsGrid
。 将 GridView 绑定到名为 ProductsDataSource
的新 SqlDataSource 控件。
图 02:将 GridView 绑定到新的 SqlDataSource 控件(单击可查看全尺寸图像)
配置向导,使其使用 Northwind 数据库。 如果已完成上一篇教程,则应已具有一个名为连接字符串NorthwindConnectionString
Web.config
。 从下拉列表中选择此连接字符串,如图 3 所示。
图 03:将 SqlDataSource 配置为使用 Northwind 数据库(单击以查看全尺寸图像)
接下来,通过从下拉列表中选择 Products 表并返回ProductName
列UnitPrice
来指定数据源控件的SELECT
语句(请参阅图 4)。 单击“下一步”,然后单击“完成”以完成“配置数据源”向导。
图 04:返回表中的ProductName
Products
字段UnitPrice
(单击可查看全尺寸图像)
就是这么简单! 完成向导后,Visual Studio 会将两个 BoundFields 添加到 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>
图 05:每个产品及其价格在 GridView 中列出(单击以查看全尺寸图像)
注意
随时清理 GridView 的外观。 一些建议包括将显示的 UnitPrice 值的格式设置为货币,并使用背景颜色和字体来改善网格的外观。 有关在 ASP.NET 中显示和设置数据格式的详细信息,请参阅我的 “使用数据”教程系列。
步骤 2:向母版页添加双价按钮
下一个任务是向母版页添加一个按钮 Web 控件,单击该控件时,会将数据库中所有产品的价格加倍。 Site.master
打开母版页,将工具箱中的按钮拖到设计器上,将其放在我们在上一教程中添加的 RecentProductsDataSource
SqlDataSource 控件下方。 将 Button ID
的属性设置为 DoublePrice
“ Text
双产品价格”。
接下来,将 SqlDataSource 控件添加到母版页,将其 DoublePricesDataSource
命名。 此 SqlDataSource 将用于执行 UPDATE
语句,将所有价格加倍。 具体而言,我们需要将其ConnectionString
和UpdateCommand
属性设置为适当的连接字符串和UPDATE
语句。 然后,当单击按钮时DoublePrice
,我们需要调用此 SqlDataSource 控件Update
的方法。 若要设置ConnectionString
和UpdateCommand
属性,请选择 SqlDataSource 控件,然后转到属性窗口。 该ConnectionString
属性列出了已存储在下拉列表中的Web.config
这些连接字符串;选择NorthwindConnectionString
如图 6 所示的选项。
图 06:将 SqlDataSource 配置为使用 NorthwindConnectionString
(单击以查看全尺寸图像)
若要设置属性UpdateCommand
,请在属性窗口中找到 UpdateQuery 选项。 如果选中此属性,则显示带省略号的按钮;单击此按钮可显示图 7 中显示的“命令和参数编辑器”对话框。 在对话框的文本框中键入以下 UPDATE
语句:
UPDATE Products SET UnitPrice = UnitPrice * 2
执行时,此语句会将表中每个记录Products
的值加倍UnitPrice
。
图 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>
保留的所有操作都是在单击按钮时DoublePrice
调用其Update
方法。 Click
为 DoublePrice
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
信息属性:CommandArgument
和CommandName
。
若要定义事件,请使用以下语法:
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);
发送 方 和 eventArgs 是要传递给订阅服务器的事件处理程序的值。
使用以下DoublePrice
Click
代码更新事件处理程序:
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:在内容页中处理事件
此时,每当单击按钮控件时DoublePrice
,母版页将引发其PricesDoubled
事件。 但是,这只是战斗的一半 - 我们仍然需要处理订阅者中的事件。 这涉及两个步骤:创建事件处理程序并添加事件连接代码,以便在引发事件时执行事件处理程序。
首先创建名为 的 Master_PricesDoubled
事件处理程序。 由于我们在母版页中定义 PricesDoubled
事件的方式,事件处理程序的两个输入参数必须分别为类型 Object
以及 EventArgs
分别。 在事件处理程序中, 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(母版页左列中)和 ProductsGrid
GridView(内容页)中的RecentProducts
价格值。 图 9 在单击按钮后 DoublePrice
立即显示同一屏幕。 如你所看到的,新价格立即反映在两个 GridViews 中。
图 08:初始价格值(单击以查看全尺寸图像)
图 09:网格视图中显示刚刚翻倍的价格(单击以查看全尺寸图像)
总结
理想情况下,母版页及其内容页彼此完全分开,不需要任何级别的交互。 但是,如果你有显示可从母版页或内容页修改的数据的母版页或内容页,则在修改数据时,可能需要让母版页提醒内容页(反之亦然),以便可以更新显示。 在前面的教程中,我们了解了如何以编程方式让内容页与其母版页进行交互;本教程介绍了如何让母版页启动交互。
虽然内容和母版页之间的编程交互可能源自内容或母版页,但使用的交互模式取决于起源。 差异是由于内容页具有单个母版页,但母版页可以具有许多不同的内容页。 更好的方法是让母版页引发事件,以指示已执行某些操作,而不是让母版页直接与内容页交互。 那些关心操作的内容页可以创建事件处理程序。
快乐编程!
深入阅读
有关本教程中讨论的主题的详细信息,请参阅以下资源:
关于作者
斯科特·米切尔是多个 ASP/ASP.NET 书籍的作者,4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 3.5。 斯科特可以在他的博客上 mitchell@4GuysFromRolla.com 或通过他的博客联系 http://ScottOnWriting.NET。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Suchi Banerjee。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请在以下位置放置我一行 mitchell@4GuysFromRolla.com