事件通常是由用户触发的,比如按钮的点击事件、下拉列表的选择项改变事件。不过有些事件并非用户触发的,而是在程序执行的某个特定阶段触发的,比如将要介绍的表格的预绑定事件、行预绑定事件以及行绑定事件,本章将会详细描述这些和表格相关的事件。
每个事件处理函数都会接受一个事件参数,默认的是EventArgs,不过Grid为大部分事件自定义了事件参数类型,先来看下源代码中的定义:
用户触发的事件参数类型:
非用户触发的事件参数类型:
这两个事件在前面章节已经提到过,就不再赘述。
这个两个事件也相对简单,其事件参数只有一个属性RowIndex,表示当前点击的是哪一行。
这个事件在实际项目中也经常用到,特别是主从表联动时,单击主表的某一行后更新从表的数据,下面通过一个例子来说明。
首先来看下示例的最终显示效果:
用户单击左侧的某一个班级时,通过更新右侧的班级信息和班级的学生列表。
目前我们只关心左侧主表的实现代码(完整的示例请查看官方示例),先看下ASPX标签结构:
1: <ext:Grid ID="Grid2" ShowBorder="true" ShowHeader="true" Title="表格(班级)" runat="server"
2: DataKeyNames="Id,Name" EnableMultiSelect="false" EnableRowClick="true" OnRowClick="Grid2_RowClick">
3: <Columns>
4: <ext:TemplateField Width="60px">
5: <ItemTemplate>
6: <asp:Label ID="Label2" runat="server" Text='<%# Container.DataItemIndex + 1 %>'></asp:Label>
7: </ItemTemplate>
8: </ext:TemplateField>
9: <ext:BoundField ExpandUnusedSpace="true" DataField="Name" DataFormatString="{0}"
10: HeaderText="姓名" />
11: </Columns>
12: </ext:Grid>
这里有如下几个关键点:
再来看下后台的行点击处理代码:
1: protected void Grid2_RowClick(object sender, ExtAspNet.GridRowClickEventArgs e)
2: {
3: BindGrid1();
4: }
5:
6: private void BindGrid1()
7: {
8: if (Grid2.SelectedRowIndex < 0)
9: {
10: return;
11: }
12:
13: int classId = Convert.ToInt32(Grid2.DataKeys[Grid2.SelectedRowIndex][0]);
14:
15: // 根据classId查询数据库,绑定从表和更新班级描述...
16: }
注意,如何启用行双击事件一定要启用EnableRowDoubleClick属性。
能够触发行内命令事件的列只有CheckBoxField和LinkButtonField两种类型,由于CheckBoxField列会有专门的篇幅讲解,这里只关心LinkButtonField。
由于LinkButtonField不是一个单独的控件,而是包含在表格控件中,所以事件的定义必须在表格上,那么LinkButtonField就需要一个标示来表明当前触发的是表格中的哪个LinkButtonField,这个标示其实就是CommandName和CommandArgument。
下面通过一个例子来说明如何使用LinkButtonField,先看下ASPX标签结构:
1: <ext:Grid ID="Grid1" Title="表格" PageSize="3" ShowBorder="true" ShowHeader="true"
2: AutoHeight="true" runat="server" EnableCheckBoxSelect="True" DataKeyNames="Id,Name"
3: Width="800px" OnRowCommand="Grid1_RowCommand" EnableRowNumber="True">
4: <Columns>
5: <ext:LinkButtonField HeaderText=" " Width="60px" CommandName="Action1" Text="按钮 1" />
6: <ext:LinkButtonField HeaderText=" " Width="60px" ConfirmText="你确定要这么做么?" ConfirmTarget="Top"
7: CommandName="Action2" Text="按钮 2" />
8: </Columns>
9: </ext:Grid>
来看下后台代码:
1: protected void Grid1_RowCommand(object sender, ExtAspNet.GridCommandEventArgs e)
2: {
3: if (e.CommandName == "Action1" || e.CommandName == "Action2")
4: {
5: object[] keys = Grid1.DataKeys[e.RowIndex];
6: labResult.Text = String.Format("你点击了第 {0} 行,第 {1} 列,行命令是 {2}", e.RowIndex + 1, e.ColumnIndex + 1, e.CommandName) +
7: "<br />" +
8: String.Format("当前行数据 - 编号:{0},姓名:{1}", keys[0], keys[1]);
9: }
10: }
来看下用户点击第二个“按钮 1”的界面效果:
这里还有一个小技巧,通过设置ConfirmText、ConfirmTarget、ConfirmIcon、ConfirmTitle来定义回发之前的确认对话框,显示效果如图:
预绑定事件一般用来修改列的属性,这样每次调用DataBind函数进行数据绑定时都会触发预绑定事件。
在《AppBox - 基于ExtAspNet的企业通用管理框架》中,此事件用来检查相应的权限并设置表格中列的属性,代码片段如下:
1: protected void Grid1_PreDataBound(object sender, EventArgs e)
2: {
3: // 数据绑定之前,进行权限检查
4: if (!CheckPowerEdit())
5: {
6: ExtAspNet.WindowField btn = Grid1.FindColumn(columnId) as ExtAspNet.WindowField;
7: btn.Enabled = false;
8: btn.ToolTip = CHECK_POWER_FAIL_ACTION_MESSAGE;
9: }
10: }
这里首先判断是否具有编辑权限,如果没有编辑权限,就设置弹出窗体列的Enabled属性为false,显示效果如下:
行预绑定事件是在每一行进行数据绑定之前触发的事件,一般用于根据原始数据修改列的属性(比如启用禁用)。
要想熟练地使用行预绑定事件,首先要能理解如下几个关键点:
先来看一个使用行预绑定事件的示例,示例的显示界面如下:
在行预绑定事件中做了如下两件事情:
首先来看下ASPX标签结构定义:
1: <ext:Grid ID="Grid1" Title="表格" PageSize="3" ShowBorder="true" ShowHeader="true"
2: Width="800px" AutoHeight="true" OnPreRowDataBound="Grid1_PreRowDataBound" runat="server"
3: EnableCheckBoxSelect="True" DataKeyNames="Id,Name" OnRowCommand="Grid1_RowCommand"
4: EnableRowNumber="True">
5: <Columns>
6: <ext:BoundField Width="100px" DataField="Name" DataFormatString="{0}" HeaderText="姓名" />
7: <ext:TemplateField Width="60px" HeaderText="性别">
8: <ItemTemplate>
9: <asp:Label ID="Label2" runat="server" Text='<%# GetGender(Eval("Gender")) %>'></asp:Label>
10: </ItemTemplate>
11: </ext:TemplateField>
12: <ext:BoundField Width="60px" DataField="EntranceYear" HeaderText="入学年份" />
13: <ext:CheckBoxField ColumnID="cbxAtSchool" TextAlign="Center" Width="60px" RenderAsStaticField="false" DataField="AtSchool"
14: HeaderText="是否在校" />
15: <ext:HyperLinkField HeaderText="所学专业" DataToolTipField="Major" DataTextField="Major"
16: DataTextFormatString="{0}" DataNavigateUrlFields="Major" DataNavigateUrlFormatString="http://gsa.ustc.edu.cn/search?q={0}"
17: DataNavigateUrlFieldsEncode="true" Target="_blank" ExpandUnusedSpace="True" />
18: <ext:ImageField Width="60px" DataImageUrlField="Group" DataImageUrlFormatString="~/images/16/{0}.png"
19: HeaderText="分组"></ext:ImageField>
20: <ext:LinkButtonField TextAlign="Center" ConfirmText="你确定要这么做么?" ConfirmTarget="Top"
21: ColumnID="lbfAction1" Width="50px" CommandName="Action1" Text="按钮" />
22: <ext:LinkButtonField TextAlign="Center" ConfirmText="你确定要这么做么?" Icon="Delete" ConfirmTarget="Top"
23: ColumnID="lbfAction2" HeaderText=" " Width="50px" CommandName="Action2" />
24: </Columns>
25: </ext:Grid>
再来看下PreRowDataBound事件处理函数:
1: protected void Grid1_PreRowDataBound(object sender, ExtAspNet.GridPreRowEventArgs e)
2: {
3: LinkButtonField lbfAction1 = Grid1.FindColumn("lbfAction1") as LinkButtonField;
4: LinkButtonField lbfAction2 = Grid1.FindColumn("lbfAction2") as LinkButtonField;
5: CheckBoxField cbxAtSchool = Grid1.FindColumn("cbxAtSchool") as CheckBoxField;
6:
7: if (e.RowIndex < 5)
8: {
9: cbxAtSchool.Enabled = true;
10: lbfAction1.Enabled = true;
11: lbfAction2.Enabled = true;
12: }
13: else
14: {
15: cbxAtSchool.Enabled = false;
16: lbfAction1.Enabled = false;
17: lbfAction2.Enabled = false;
18: }
19:
20: // 如果绑定到 DataTable,那么这里的 DataItem 就是 DataRowView
21: HyperLinkField linkField = Grid1.Columns[4] as HyperLinkField;
22: DataRowView row = e.DataItem as DataRowView;
23: if (row != null)
24: {
25: linkField.DataTextFormatString = "{0} (" + row["EntranceYear"].ToString() + ")";
26: }
27: }
由于当前正处于数据绑定的前夕,所以可以通过e.DataItem获得当前将要进行绑定的原始数据:
注意:由于GridRow也有DataItem属性,很多人就误认为任何时候都可以使用此属性了,这是错误的。
从本质上讲,DataItem是临时变量,只有在本次HTTP请求中,并且在数据绑定后才存在,其他情况下都为null。
甚至在PreRowDataBound事件内部,行实例的DataItem也是null,可以在上面函数中增加如下代码:
1: GridRow rowInstance = Grid1.Rows[e.RowIndex];
2: if (rowInstance.DataItem == null)
3: {
4: // 会走到这里...
5: }
行绑定事件是在行的数据绑定结束后触发的,此时可以修改行的渲染结果(也就是行单元格渲染后的HTML代码),下面通过一个示例说明:
可以特别比较本例和上例的区别,本例中在RowDataBound事件中修改渲染后的“所学专业”列,直接看后台代码:
1: protected void Grid1_RowDataBound(object sender, ExtAspNet.GridRowEventArgs e)
2: {
3: // e.DataItem -> System.Data.DataRowView 或者自定义类
4: // e.RowIndex -> 当前行序号(从 0 开始)
5: // e.Values -> 当前行每一列渲染后的 HTML 片段
6:
7: DataRowView row = e.DataItem as DataRowView;
8: if (row != null)
9: {
10: e.Values[4] = String.Format("{0} ({1})", e.Values[4], row["EntranceYear"]);
11: }
12: }
想知道Values到底是什么值,调试一下就能看到:
可以看出,Values属性保存的是渲染后的HTML片段,并且Values属性在HTTP请求之间是持久化的,这也是为什么Ajax回发过程中不需要重新绑定表格的原因。
注意:虽然Values属性是持久化的,但是不可以作为原始数据使用,因为Values的本质是渲染后的HTML片段。推荐的方式是定义DataKeyNames属性,并使用持久化属性DataKeys。
本章我们不仅学习了用户触发的事件类型,包括分页排序事件、行单击双击事件以及行命令事件;同时还学习了非用户触发的事件类型,包括预绑定事件、行预绑定事件以及行绑定事件,这三个事件能够用来单独控制每一行中每个单元格的显示内容,同样非常重要。
下一篇文章我们会介绍CheckBoxField,前面已经提到CheckBoxField同样能够触发行命令事件,这是否意味着可以直接编辑CheckBoxField呢?下一篇文章会详细讲解。