事件冒泡:将消息从用户控件发送到页面容器






4.36/5 (8投票s)
本文介绍了如何从用户控件向包含该控件的页面发送消息。
业务问题
有一个俱乐部列表,用户希望提交请求将新俱乐部添加到列表中。在第一个视图中,俱乐部以网格视图控件的形式列出。用户单击“添加俱乐部”链接以使用表单视图控件提交俱乐部。视图更改为允许用户提交,俱乐部列表将被隐藏。表单视图控件包含在一个通用的 Web 用户控件中。当用户选择“提交”或“取消”时,必须将一条消息发送到包含该控件的页面。消息中的信息用于显示一条指示成功或取消提交的消息。当用户完成或取消提交后,视图将更改回俱乐部列表。将显示相应的消息,并隐藏提交表单。
用例说明
应用程序启动时,网格视图已填充。用户单击“添加新俱乐部”链接以提交新俱乐部。
用户输入俱乐部信息并选择提交或取消条目。包含 Web 用户控件的页面会收到一条指示用户选择的消息。
这表明用户选择提交俱乐部,并且表单视图用户控件发送的消息已冒泡到包含的页面。页面确定提交成功,并显示确认消息。
当用户选择取消提交时,包含表单视图的 Web 用户控件会将消息发送到包含该控件的页面。在此,用户选择取消俱乐部提交。
背景
挑战与解决方案:要实现此业务流程,开发人员必须利用事件冒泡。使用事件冒泡,消息将从 Web 用户控件发送回俱乐部列表页面。消息中的信息用于更改页面的状态并显示提交确认。不幸的是,网上关于如何实现此过程的文档很少,而且充其量也只是粗略的。本文向开发人员详细介绍了如何实现此过程。
事件冒泡概览:事件冒泡只是包含一条从一个对象发送到另一个对象的消息。消息包含在继承自 EventArgs
的类中。EventArgs
是一个包含事件数据的系统基类。当然,这是对该概念的简化。
为说明起见,页面类同时包含网格视图和自定义 Web 用户控件。Web 用户控件是表单视图控件的容器。表单视图控件包含“提交”和“取消”链接。
我们希望将一条消息从“提交”或“取消”链接发送到包含该控件的控件层次结构,直到俱乐部列表页面容器。据说该消息会冒泡到页面容器。在那里,事件处理程序会解释该消息,并向用户显示成功或取消消息。
本文对事件冒泡理论的介绍就到这里。下面开始编写代码。
关键元素
UserControlEventArgs
类包含两部分。类声明和事件委托的声明
UserControlEventArgs 类 | |
实例变量 | |
InsertComplete |
此标志指示新记录已成功插入或提交已取消。 |
事件委托
事件委托 | |
ClubInsertedEventHandler |
接受两个参数。第一个是 object 类型的发送者。第二个是 UserControlEventArgs ,用于在冒泡中传递消息。 |
WebUserControlAddUser
类需要一个事件处理程序的实例变量。
WebUserControlAddUser | |
实例变量 | |
public event ClubInsertedEventHandler ClubInsertComplete; |
将此变量声明为 |
触发事件 | |
UserControlEventArgs args = new UserControlEventArgs(true); |
声明将传递到事件冒泡的 args 变量。 |
ClubInsertComplete(this, args); |
引发事件。 |
最后,ClubList 页面使用该事件。
Club List 页面 | |
Click Event (ASPX) | |
OnClubInsertComplete="ClubInsertComplete" |
Web 用户控件 |
处理 Web 用户控件事件 | |
ClubInsertComplete(object sender, UserControlEventArgs e) |
"e " 暴露了作为参数传递到事件处理程序的 InsertComplete 变量。 |
编写代码
关键概念
UserControlEventArgs
类用于将消息从 Web 用户控件传递到包含用户控件的默认 Web 页面。一个名为 ClubInsertedEventHandler
的委托用于声明用户控件中引发的事件的“args
”委托类型。该事件称为“ClubInsertComplete
”。
委托事件声明位于 UserControlEventArgs
类中。请注意,声明在类的外部。将委托包含在 UserControlEventArgs
类的主体内是错误的,它将导致事件对作为 Web 用户控件容器的默认页面隐藏。
当记录已插入到数据表中时,args
值将被实例化并作为参数传递到“ClubInsertComplete
”事件中。请注意,事件的委托遵循 Microsoft 的事件声明模式。
当引发“ClubInsertComplete
”事件时,事件会冒泡到作为“WebUserControlAddUser
”容器的默认页面,并在那里进行处理。VS2008 的智能感知不会以通常的方式显示事件。但是,用户可以转到默认页面的 GUI 部分,在该部分已添加了用户控件。在那里,VS2008 的智能感知将列出设置为默认代码隐藏中的“ClubInsertComplete
”方法的“OnClubInsertComplete
”事件。
步骤如下
步骤 1。ClubList
类是一个辅助类,它只是创建一个数据表并用俱乐部列表填充它。它们将在默认页面上的网格视图控件中显示。
静态类 ClubList
方法 | 描述 |
BuildClubList() |
使用默认的俱乐部列表填充数据表。稍后将俱乐部列表绑定到 GridView 控件。 |
AddClub() |
将俱乐部添加到数据表中。 |
ClearTable() |
特定于 VS2008 调试器。在使用静态类时,必须清除表,因为在调试器关闭并重新运行时,对象尚未被处置。 |
public static class ClubList
{
private static DataTable _dt = new DataTable("ClubList");
static public DataTable dt
{
get { return _dt; }
set { _dt = value; }
}
//CLEAR THE TABLE BECAUSE OF A
//PECULARITY WITH VS2008 WHEN
//RUNNING IN DEBUG MODE.
static public void ClearTable()
{
_dt.Columns.Clear();
_dt.Rows.Clear();
}
static public DataTable BuildClubList()
{
DataColumn col;
DataColumn col2;
DataColumn col3;
DataRow row;
// Create new Datacol, set DataType,
// colName and add to DataTable.
col = new DataColumn();
col.DataType = Type.GetType("System.Int32");
col.ColumnName = "ClubID";
col.ReadOnly = true;
col.Unique = false;
// Add the col to the DatacolCollection.
_dt.Columns.Add(col);
col2 = new DataColumn();
col2.DataType = Type.GetType("System.String");
col2.ColumnName = "ClubName";
col2.AutoIncrement = false;
col2.Caption = "Club Name";
col2.ReadOnly = false;
col2.Unique = false;
_dt.Columns.Add(col2);
col3 = new DataColumn();
col3.DataType = Type.GetType("System.String");
col3.ColumnName = "URL";
col3.AutoIncrement = false;
col3.Caption = "URL";
col3.ReadOnly = false;
col3.Unique = false;
_dt.Columns.Add(col3);
row = _dt.NewRow();
row["ClubID"] = 1000;
row["ClubName"] = "Joe's Rocketry";
row["URL"] = "www.joes.com";
_dt.Rows.Add(row);
row = _dt.NewRow();
row["ClubID"] = 1001;
row["ClubName"] = "American Rocketry";
row["URL"] = "www.ar.com";
_dt.Rows.Add(row);
return _dt;
}
public static DataTable AddClub(int ClubID, string ClubName, string url)
{
DataRow row;
row = _dt.NewRow();
row["ClubID"] = ClubID;
row["ClubName"] = ClubName;
row["URL"] = url;
_dt.Rows.Add(row);
row = _dt.NewRow();
return _dt;
}
}
步骤 2。创建一个继承自 EventArgs
的类。此类还有一个在类主体外部声明的事件委托。
UserControlEventArgs
类包含一个名为 InsertComplete
的单个属性,该属性封装了从 Web 用户控件发送到俱乐部列表页面的消息。EventArgs
继承是系统中的一个基类。
/// <summary>
/// Jeff Kent - 11/14/2009
/// EVENT CLASS THAT ENCAPSULATES THE EVENT ARGUMENTS.
/// </summary>
public class UserControlEventArgs : EventArgs
{
//CONSTRUCTOR USED TO DETERMINE IF A
//RECORD INSERTION WAS SUCCESSFUL.
public UserControlEventArgs(bool InsertComplete)
{
if (InsertComplete)
{
this.InsertComplete = true;
}
else
{
this.InsertComplete = false;
}
}
private bool _InsertComplete;
public bool InsertComplete{
get { return _InsertComplete ; }
set { _InsertComplete = value; }
}
//DECLARE THE EVENT DELEGATE FOR THE CONTROL.
//NOTE HOW THE DELEGATE RESIDES OUTSIDE THE SCOPE OF THE CLASS.
public delegate void ClubInsertedEventHandler(object sender, UserControlEventArgs e);
步骤 3。将 Web 用户控件添加到项目中。我使用带有插入项模板的表单视图控件来收集用户数据。请注意,ItemCommand
事件是如何用于管理表单视图控件的状态的。我这样做是因为该控件存在一些特性。
这里的关键是 ClubInsertedEventHandler
,它是一个事件委托,用于声明变量 ClubInsertComplete
。当表单视图 ItemCommand
事件触发时,switch
结构会流转到 CommitInsert
或 CancelInsert
。在那里,使用构造函数实例化 args
变量。true 或 false 的值标志着用户选择提交俱乐部还是取消提交。ClubInsertComplete
启动了事件冒泡过程。消息会发送回俱乐部列表页面,并在那里进行处理。
/// <summary>
/// Jeff Kent - 11/14/2009
/// SAMPLE WEB USER CONTROL WITH EVENT BUBBLING SUPPORT.
/// THE FORM VIEW CONTROL IS USED TO COLLECT AND PROCESS CLUB INFORMATION.
/// ONLY THE INSERT ITEM TEMPLATE IS USED IN THIS EXAMPLE.
/// </summary>
public partial class WebUserControlAddUser : System.Web.UI.UserControl
{
//DECLARE THE DELEGATE VARIABLE.
public event ClubInsertedEventHandler ClubInsertComplete;
//PUT THE FORM VIEW INTO THE INSERT MODE
//EVERY TIME THE PAGE LOADS THE CONTROL.
protected void FormView1_Load(object sender, EventArgs e)
{
FormView1.ChangeMode(FormViewMode.Insert);
}
// THE FORM VIEW CONTROL CAN BE TRICKY.
// THE WORK AROUND THE QUIRKS IS TO INTERCEPT THE ITEMCOMMAND EVENT
// THEN USE THE SWITCH STRUCTURE TO CONTROL THE STATE OF THE CONTROL.
protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)
{
UserControlEventArgs args;
switch (e.CommandName)
{
case "StartInsert":
//NOT USED IN THIS IMPLEMENTATION.
break;
case "CommitInsert":
InsertNewClub();
FormView1.ChangeMode(FormViewMode.ReadOnly);
//FIRE THE EVENT TO SEND NOTICE TO
//THAT THE CLUB SUBBMISSION WAS SUCCESSFUL.
args = new UserControlEventArgs(true);
ClubInsertComplete(this, args);
break;
case "CancelInsert":
FormView1.ChangeMode(FormViewMode.ReadOnly);
FormView1.DataBind();
//FIRE THE EVENT TO SEND NOTICE TO
//THAT THE CLUB SUBBMISSION WAS CANCELLED.
args = new UserControlEventArgs(false);
ClubInsertComplete(this, args);
break;
default:
break;
}
}
//OBTAIN THE DATA FROM THE FORM
//AND INSERT IT INTO THE DATA TABLE.
protected void InsertNewClub()
{
TextBox tb;
tb = (TextBox)FormView1.FindControl("ClubID");
int ClubID = Convert.ToInt32(tb.Text);
tb = (TextBox)FormView1.FindControl("ClubName");
string ClubName = tb.Text;
tb = (TextBox)FormView1.FindControl("URL");
string url = tb.Text;
ClubList.AddClub(ClubID, ClubName, url);
}
}
步骤 4。创建用户控件并使用表单视图控件来收集数据。请注意,此示例仅使用插入项模板。
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="WebUserControlAddUser.ascx.cs"
Inherits="WebUserControlAddUser" %>
<asp:FormView ID="FormView1" runat="server" Width="300px"
EmptyDataText="Empty rocketry data"
OnItemCommand="FormView1_ItemCommand" OnLoad="FormView1_Load">
<InsertItemTemplate>
<table cellpadding="2" cellspacing="0"
style="vertical-align: top; border-color: Black;border-style: outset;
border-width: 3px; width: 400px">
<tr>
<td style="text-align: left; vertical-align: top; width: 33%">
Club ID (xxx - int):
</td>
<td style="text-align: left; vertical-align: top">
<asp:TextBox ID="ClubID" runat="server" Columns="30"
Style="text-align: left" Text='<%# Bind("ClubID") %>'
TabIndex="10"> </asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2"
runat="server" Text="*" ErrorMessage="Enter an integer"
ControlToValidate="ClubID"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td style="text-align: left; vertical-align: top">
Club Name:
</td>
<td style="text-align: left; vertical-align: top">
<asp:TextBox ID="ClubName" runat="server" Columns="30"
Style="text-align: left" Text='<%# Bind("ClubName") %>'
TabIndex="10"> </asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
runat="server" ControlToValidate="ClubName"
Text="*"
ErrorMessage="The club name is required.">
</asp:RequiredFieldValidator>
<br />
</td>
</tr>
<tr>
<td style="text-align: left; vertical-align: top">
URL:
</td>
<td style="text-align: left; vertical-align: top">
<asp:TextBox ID="url" runat="server"
Style="text-align: left" Text='<%# Bind("url") %>'
TabIndex="20"></asp:TextBox>
<br />
<br />
</td>
</tr>
<tr>
<td colspan="2">
<asp:LinkButton ID="LinkButton4" runat="server"
CommandName="CommitInsert" CausesValidation="true"
TabIndex="100">Submit</asp:LinkButton>
<asp:LinkButton ID="LinkButton3" runat="server"
CommandName="CancelInsert" CausesValidation="false"
TabIndex="200">Cancel</asp:LinkButton>
</td>
</tr>
<tr>
<td colspan="2">
<asp:ValidationSummary ID="ValidationSummary1" runat="server" />
</td>
</tr>
</table>
</InsertItemTemplate>
<EditRowStyle />
</asp:FormView>
步骤 5。最后,创建一个带有 multiview 控件的默认页面,并将用户控件放入视图的第一部分。在第二视图中放置一个数据网格。
ClubInsertComplete
拦截了从 Web 用户控件冒泡上来的消息。消息被传递到 ClubInsertComplete
,并通过值 e
暴露,该值为 UserControlEventArgs
类型。正是 UserControlEventArgs
对象封装了从 Web 用户控件发送回俱乐部列表页面的消息。在这里,设置“提交成功”或“取消”标签以使其可见。此外,还会根据需要设置视图。
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// SET THE INITIAL VIEW TO THE GRID WITH
// A SAMPLE LISTING OF CLUBS.
MultiView1.SetActiveView(view2);
ClubList.ClearTable();
gvClubList.DataSource = ClubList.BuildClubList();
gvClubList.DataBind();
SubmissionSuccessful.Visible = false;
}
}
//CHANGE THE VIEW SO THAT THE FORM FOR A CLUB ENTRY IS EXPOSED.
protected void LinkButton1_Click(object sender, EventArgs e)
{
MultiView1.SetActiveView(view1);
}
//CUSTOM EVENT THAT BUBBLES FROM ROCKETRY CLUB CONTROL.
protected void ClubInsertComplete(object sender, UserControlEventArgs e)
{
//NOTIFY THE USER WHEN THE SUBMISSION IS SUCCESSFUL.
if (e.InsertComplete)
{
SubmissionSuccessful.Visible = true;
}
MultiView1.SetActiveView(view2);
gvClubList.DataSource = ClubList.dt;
gvClubList.DataBind();
}
}
特别需要注意的是,VS2008 的智能感知不会以通常的方式公开 Web 用户控件的事件。但是,在 ASPX 页面中,OnClubInsertComplete
事件会出现在智能感知中。将其设置为 ClubInsertComplete
以处理从 Web 用户控件冒泡到页面的事件。
<pre>
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Src="WebUserControlAddUser.ascx"
TagName="WebUserControlAddUser" TagPrefix="uc1" %>
<!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></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:MultiView ID="MultiView1" runat="server">
<asp:View ID="view1" runat="server">
<!-- THE "OnClubInsertComplete" EVENT IS EXPOSED TO
INTELLISENSE FOR THE USER CONTROL. -->
<uc1:WebUserControlAddUser ID="AddClubUser" runat="server"
OnClubInsertComplete="ClubInsertComplete" />
</asp:View>
<asp:View ID="view2" runat="server">
<asp:Label ID="SubmissionSuccessful" runat="server"
Text="Your Club Has Been Submitted."></asp:Label><br />
<asp:LinkButton ID="LinkButton1" runat="server"
Text="Add New Club" OnClick="LinkButton1_Click">
Add New Club</asp:LinkButton><br />
<asp:GridView ID="gvClubList" runat="server">
</asp:GridView>
</asp:View>
</asp:MultiView>
</div>
</form>
</body>
</html>
结论
在开发已部署到生产环境的实际解决方案所需的所有材料都很难找到。开发过程涉及将各种概念组合在一起,并且网上明显缺乏参考资料。充其量,我找到的材料已经过时。我希望开发人员能够通过此示例了解事件冒泡(与 Web 用户控件相关)如何在生产 Web 站点上得到利用。