本文笔记选自《庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术》一书
一般在实际开发中,对于事件不需要传递数据信息时,像上面的KingTextBox控件的事件,在引发事件时传递的参数为EventArgs.Empty,如下所示:
OnTextChanged(EventArgs.Empty);
这是因为控件KingTextBox的TextChanged事件比较简单,这里不需要参数对象传递数据。但像一些复杂的控件比如GridView的按钮命令事件,必须得有参数命令表示单击了哪个按钮;翻页事件,需要EventArgs参数对象把当前页信息传递到页面后台代码的事件体的第二个参数中,然后开发人员根据此页参数从数据库取得对应页的数据;还有像ItemDataBound事件,也需要把当前Row信息和索引等数据作为EventArgs参数传递到事件实例中。
当遇到以上这些情况,我们就需要定义自己的事件参数类和委托,而不使用默认的System.EventArgs类。下面就以Grid控件的翻页功能说明一下定义事件参数类和委托的方法,请看以下代码:
/// <summary>
/// class="Apple-converted-space">
/// </summary>
public delegate void GridPageChangedEventHandler(object source, GridPage ChangedEventArgs e);
public class GridPageChangedEventArgs : System.EventArgs
{
public GridPageChangedEventArgs()
{
}
private int intCurrentPageIndex;
public new int CurrentPageIndex
{
get { return intCurrentPageIndex; }
set { intCurrentPageIndex = value; }
}
private int intPageCount;
public new int PageCount
{
get { return intPageCount; }
set { intPageCount = value; }
}
private int intPageSize;
public new int PageSize
{
get { return intPageSize; }
set { intPageSize = value; }
}
}
以上代码定义了一个存储翻页相关信息的参数类,此参数类派生于System.EventArgs类,在这里系统不强制要求一定继承于该类,不继承它也是可以的,但继承于该类有一点好处。先看一下System.EventArgs基类的代码:
/// <summary>
///
/// </summary>
public class EventArgs
{
//表示没有事件数据的事件
public static readonly EventArgs Empty;
//初始化 System.EventArgs 类的新实例
public EventArgs();
}
EventArgs中除了一个构造方法外,还有一个它本身类型的Empty属性,从这里可以知道前面在调用时通过OnTextChanged(EventArgs.Empty);格式,把EventArgs.Empty作为空参数传入事件引发方法时的用途了。这里要说明的是如果我们的GridPageChangedEventArgs类是从EventArgs继承而来,则不但可以传递GridPageChangedEventArgs类对象,而且可以使用GridPageChangedEventArgs.Empty的形式传递空参数对象。
在GridPageChangedEventArgs方法体中定义了一些属性;分别表示当前页(CurrentPageIndex)、页总数(PageCount)和页尺寸(PageSize)。这些都是我们自定义的页信息数据。
代码中的这句:
public delegate void GridPageChangedEventHandler(object source, GridPage ChangedEventArgs e);
定义了一个委托GridPageChangedEventHandler。该委托可以指定这样的一个事件方法:第一个参数为object类型,第二个参数即为上面我们定义的页参数类对象GridPageChanged EventArgs。在注册事件时,该委托可以保证在页面后面代码中自动产生的事件体的两个参数类型与自己的两个参数类型一致。下面是在页面中注册的事件后台代码:
protected void Grid1_PageIndexChanged(object source,GridPageChangedEventArgs e)
{
int intCurrentPageIndex = e.CurrentPageIndex;
int intPageSize = e.PageSize;
//获取数据逻辑
}
可以看到第二个参数类型即我们定义的GridDocPageChangedEventArgs类型,在事件方法体中,可以直接通过e.CurrentPageIndex和e.PageSize获取数据,这种应用就比较多了。
现在页参数对象和委托定义好了,下面说一下在主控件内部是如何应用它们的。声明事件代码如下:
/// <summary>
///
/// </summary>
private new static readonly object EventPageIndexChanged = new object();
[Category("Action"), Description("翻页事件")]
public event GridPageChangedEventHandler PageIndexChanged
{
add
{
base.Events.AddHandler(Grid.EventPageIndexChanged, value);
}
remove
{
base.Events.RemoveHandler(Grid.EventPageIndexChanged, value);
}
}
这里继续采用5.3.1小节讲的高效率事件集合列表对象base.Events,事件的名称为PageIndexChanged,委托类型为之前我们定义的委托类型GridPageChangedEventHandler。
引发事件的核心代码如下:
/// <summary>
///
/// </summary>
protected override bool OnBubbleEvent(object source, EventArgs e)
{
bool handled = false;
if (e is GridCommandEventArgs)
{
if ((((GridCommandEventArgs)(e)).CommandSource) is LinkButton)
{
LinkButton lb=((LinkButton)(((GridCommandEventArgs)(e)). Command Source));
if (lb.CommandName == "Page")
{
if (lb.CommandArgument == "ButtonFirst")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex != 0)
{
this.CurrentPageIndex = 0;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
if (lb.CommandArgument == "ButtonNext")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex < this.PageCount - 1)
{
this.CurrentPageIndex += 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
if (lb.CommandArgument == "ButtonPrev")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex > 0)
{
this.CurrentPageIndex -= 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
if (lb.CommandArgument == "ButtonLast")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex != this.PageCount - 1)
{
this.CurrentPageIndex = this.PageCount - 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
}
}
}
return handled || base.OnBubbleEvent(source, e);
}
以上OnBubbleEvent方法主要应用于复合控件中,采用冒泡形式处理子控件事件,后面介绍复合控件冒泡处理事件机制时再详细讲解此方法。另外,在控件的翻页栏中预先放置了四个翻页功能的按钮,分别表示“首页”、“上一页”、“下一页”、“末页”,并设置它们的属性CommandName都为“Page”,CommandArgument分别为“ButtonFirst”,“ButtonPrev”,“ButtonNext”,“ButtonLast”。
这样就可以根据按钮的命令和参数确定执行什么样的逻辑。这里仅拿按钮“下一页”(Command="Page"&&CommandArgument="ButtonNext")为例解释一下代码逻辑:
/// <summary>
///
/// </summary>
if (lb.CommandArgument == "ButtonNext")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex < this.PageCount - 1)
{
this.CurrentPageIndex += 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
代码中首先定义一个页参数类对象,然后通过条件语句判断当前是否是最后一页,如果不是最后一页,则从主控件上读取当前页信息(当前页、页数、页记录数),并赋值给GridPageChangedEventArgs对象,然后以页参数对象作为参数调用this.OnPageIndexChanged方法引发事件。另外,注意LinkButton的CommandName和CommandArgument属性的组合用法。
最后,看一下OnPageIndexChanged方法代码:
/// <summary>
/// 获得本书更多内容,请看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
protected new void OnPageIndexChanged(GridPageChangedEventArgs e)
{
GridPageChangedEventHandler handler1 = (GridPageChangedEventHandler)base. Events[Grid.EventPageIndexChanged];
if (handler1 != null)
{
handler1(this, e);
}
}
此方法的功能是从base.Events对象中取出以Grid.EventPageIndexChanged为Key的事件引用句柄,假如句柄不为null(开发人员注册了翻页事件),则引发事件方法体。
最后,说明非常重要的一点:如果自定义了事件参数类,并要求在开发人员注册的事件体中自动显示改变参数对象的类型,如:
protected void Grid1_PageIndexChanged(object source, GridPageChangedEventArgs e)
{
//… …
}
中的第二个参数显示为GridPageChangedEventArgs类型,而不是默认的EventArgs类型,我们也必须定义自己的委托(如本例定义了委托GridPageChangedEventHandler);而默认的委托EventHandler对应的参数类型为基类System.EventArgs,即如果这里将默认的委托EventHandler和GridPageChangedEventArgs类一起使用的话,则生成以下的代码语句:
protected void Grid1_PageIndexChanged(object source, EventArgs e)
{
//… …
}
可以看出参数变为EventArgs类型了。这样就不能利用我们自己定义的GridPage ChangedEventArgs类中的数据了。虽然可以使用(GridPageChangedEventArgs)EventArgs的方式转换一下也可以取得GridPageChangedEventArgs对象中的数据,但据笔者了解还没有开发人员这么用,因为没有人知道还有个GridPageChangedEventArgs类。
这一节主要讲解如何定制自己的参数类和委托,并以Grid的分页功能为例演示其在实际开发中的应用。下一节开始讲解复合控件的事件机制。