事件通常是由用戶觸發的,比如按鈕的點擊事件、下拉列表的選擇項改變事件。不過有些事件並非用戶觸發的,而是在程序執行的某個特定階段觸發的,比如將要介紹的表格的預綁定事件、行預綁定事件以及行綁定事件,本章將會詳細描述這些和表格相關的事件。
有哪些事件參數類型
每個事件處理函數都會接受一個事件參數,默認的是EventArgs,不過Grid為大部分事件自定義了事件參數類型,先來看下源代碼中的定義:
用戶觸發的事件參數類型:
- GridCommandEventArgs:表格行命令事件參數,對應RowCommand事件。
- RowIndex:行索引
- ColumnIndex:列索引
- CommandName:命令名稱
- CommandArgument:命令參數
- GridPageEventArgs:表格分頁事件參數,對應PageIndexChange事件。
- NewPageIndex:新頁面的索引
- GridSortEventArgs:表格排序事件參數,對應Sort事件。
- SortField:排序字段
- SortDirection:排序方向
- ColumnIndex:列索引
- GridRowClickEventArgs:表格行點擊事件參數,對應RowClick和RowDoubleClick兩個事件。
- RowIndex:行索引
非用戶觸發的事件參數類型:
- GridPreRowEventArgs:表格行預綁定事件參數,對應PreRowDataBound事件。
- GridRowEventArgs:表格行綁定事件參數,對應RowDataBound事件。
- 還有一個PreDataBound事件,使用的是默認EventArgs事件參數。
分頁和排序事件
這兩個事件在前面章節已經提到過,就不再贅述。
行單擊和行雙擊事件
這個兩個事件也相對簡單,其事件參數只有一個屬性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>
這里有如下幾個關鍵點:
- 設置表格的EnableMultiSelect屬性為false,讓表格只能單選;
- 設置表格的EnableRowClick屬性為true;
- 設置行點擊事件處理函數OnRowClick。
再來看下后台的行點擊處理代碼:
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,顯示效果如下:
行預綁定事件
行預綁定事件是在每一行進行數據綁定之前觸發的事件,一般用於根據原始數據修改列的屬性(比如啟用禁用)。
要想熟練地使用行預綁定事件,首先要能理解如下幾個關鍵點:
- 行預綁定事件發生在每一行進行數據綁定之前;
- 列屬性是全局的,這就意味着在一次行預綁定事件中對列屬性的修改會影響以后的每一次的行綁定;
- 列屬性不能在客戶端和服務器之間持久化,所以每次對表格進行數據綁定時都要重復對列屬性的修改。
先來看一個使用行預綁定事件的示例,示例的顯示界面如下:
在行預綁定事件中做了如下兩件事情:
- 修改“所學專業”列的DataTextFormatString,在專業后面增加年份;
- 根據一定的規則,啟用/禁用“是否在校”列、按鈕列以及刪除按鈕列。
首先來看下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獲得當前將要進行綁定的原始數據:
- 如果是將表格綁定到DataTable、DataView、DataSet,則這里的DataItem就是DataRowItem;
- 如果是將表格綁定到IEnumerable的數據,則這里的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呢?下一篇文章會詳細講解。