排序和分頁是表格必備的兩個重要功能,本章會詳細闡述如何在ExtAspNet中實現這兩個功能。
排序
首先來看一個排序的例子,ASPX標簽如下:
1: <ext:Grid ID="Grid1" Title="表格" AllowSorting="true" SortColumn="year"
2: SortDirection="ASC" Width="750px" AutoHeight="true" runat="server" EnableCheckBoxSelect="True"
3: DataKeyNames="Id,Name,AtSchool" EnableRowNumber="True" OnSort="Grid1_Sort">
4: <Columns>
5: <ext:BoundField Width="100px" ColumnID="name" SortField="Name" DataField="Name" DataFormatString="{0}"
6: HeaderText="姓名" />
7: <ext:TemplateField Width="60px" SortField="Gender" 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="100px" ColumnID="year" SortField="EntranceYear" DataField="EntranceYear" HeaderText="入學年份" />
13: <ext:CheckBoxField Width="60px" SortField="AtSchool" RenderAsStaticField="true" 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: </Columns>
21: </ext:Grid>
這里面有幾個關鍵點:
- 表格控件設置排序相關的屬性以及OnSort事件處理函數
- AllowSorting:是否允許排序。
- SortColumn:當前排序的列ID,當然也可以不設置此屬性,而是在后台初始化代碼中直接指定默認排序字段。
- SortDirection:排序方向,ASC(默認值)或者DESC。
- 對於每一個需要排序的列,設置SortField屬性。
來看下后台表格初始化代碼:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: BindGrid();
6: }
7: }
8:
9: private void BindGrid()
10: {
11: GridColumn column = Grid1.FindColumn(Grid1.SortColumn);
12: BindGridWithSort(column.SortField, Grid1.SortDirection);
13: }
14:
15: private void BindGridWithSort(string sortField, string sortDirection)
16: {
17: DataTable table = GetDataTable();
18:
19: DataView view1 = table.DefaultView;
20: view1.Sort = String.Format("{0} {1}", sortField, sortDirection);
21:
22: Grid1.DataSource = view1;
23: Grid1.DataBind();
24: }
注意,在調用BindGridWithSort私有函數時,我們通過FindColumn找到默認排序的列,從而找到此列的排序數據字段。
當然,如果沒有設置表格的SortColumn,這里可以直接硬編碼默認的排序字段,比如:
1: private void BindGrid()
2: {
3: BindGridWithSort("EntranceYear", Grid1.SortDirection);
4: }
最后是用戶點擊列標題時觸發的排序事件,來看下其事件處理函數:
1: protected void Grid1_Sort(object sender, ExtAspNet.GridSortEventArgs e)
2: {
3: BindGridWithSort(e.SortField, e.SortDirection);
4: }
是不是非常簡單,最后的顯示效果如下:
內存分頁
所謂的內存分頁,就是在頁面初始化時將表格數據一次性全部載入,保存在頁面狀態視圖中,從而保證在后續的分頁操作時不用再次訪問數據庫。在數據量少的情況下(一般少於100條數據),這一方式非常划算,因此需要的編碼非常少。下面還是通過一個示例來說明。
先來看下ASPX標簽的結構:
1: <ext:Grid ID="Grid1" Title="表格" PageSize="5" ShowBorder="true" ShowHeader="true"
2: AutoHeight="true" AllowPaging="true" runat="server" EnableCheckBoxSelect="True"
3: Width="800px" DataKeyNames="Id,Name" OnPageIndexChange="Grid1_PageIndexChange"
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 Width="60px" RenderAsStaticField="true" DataField="AtSchool" HeaderText="是否在校" />
14: <ext:HyperLinkField HeaderText="所學專業" DataTooltipField="Major" DataTextField="Major"
15: DataTextFormatString="{0}" DataNavigateUrlFields="Major" DataNavigateUrlFormatString="http://gsa.ustc.edu.cn/search?q={0}" DataNavigateUrlFieldsEncode="true"
16: Target="_blank" ExpandUnusedSpace="True" />
17: <ext:ImageField Width="60px" DataImageUrlField="Group" DataImageUrlFormatString="~/images/16/{0}.png"
18: HeaderText="分組"></ext:ImageField>
19: </Columns>
20: </ext:Grid>
這里面有幾個關鍵點:
- 表格設置屬性AllowPaging=true和PageSize;
- 表格設置分頁事件處理函數OnPageIndexChange;
- 各列沒有特殊的屬性設置。
看下后台初始化代碼:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: BindGrid();
6: }
7: }
8:
9: private void BindGrid()
10: {
11: DataTable table = GetDataTable();
12:
13: Grid1.DataSource = table;
14: Grid1.DataBind();
15: }
可以看到,初始化代碼和不分頁時的代碼一模一樣。
再來看下分頁事件處理函數:
1: protected void Grid1_PageIndexChange(object sender, ExtAspNet.GridPageEventArgs e)
2: {
3: Grid1.PageIndex = e.NewPageIndex;
4: }
也很簡單,對吧。
其實,如果你之前用過AspNet的GridView的話,這里的代碼和使用GridView的代碼一模一樣。
數據庫分頁
在展示大數據時,數據庫分頁是必須的。數據庫分頁時,頁面第一次初始化時只加載表格當前頁的數據,所以每次用戶點擊分頁按鈕時,后台代碼都要進行數據庫查詢並重新綁定當前頁的數據。由於查詢當前頁的數據並綁定表格需要反復調用,通常我們把這一操作提取到一個單獨的函數中。
首先看下ASPX標簽的聲明:
1: <ext:Grid ID="Grid1" Title="表格" Width="800px" PageSize="5" ShowBorder="true" ShowHeader="true"
2: AutoHeight="true" AllowPaging="true" runat="server" EnableCheckBoxSelect="True"
3: DataKeyNames="Id,Name" IsDatabasePaging="true" OnPageIndexChange="Grid1_PageIndexChange"
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 Width="60px" RenderAsStaticField="true" DataField="AtSchool" HeaderText="是否在校" />
14: <ext:HyperLinkField HeaderText="所學專業" DataTooltipField="Major" DataTextField="Major"
15: DataTextFormatString="{0}" DataNavigateUrlFields="Major" DataNavigateUrlFormatString="http://gsa.ustc.edu.cn/search?q={0}" DataNavigateUrlFieldsEncode="true"
16: Target="_blank" ExpandUnusedSpace="True" />
17: <ext:ImageField Width="60px" DataImageUrlField="Group" DataImageUrlFormatString="~/images/16/{0}.png"
18: HeaderText="分組"></ext:ImageField>
19: </Columns>
20: </ext:Grid>
這里面有幾個關鍵點:
- 表格設置屬性AllowPaging、IsDatabasePaging、PageSize三個屬性;
- 表格設置分頁事件處理函數OnPageIndexChange;
- 各列沒有特殊的屬性設置。
看下后台初始化代碼:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: BindGrid();
6: }
7: }
8:
9: private void BindGrid()
10: {
11: // 1.設置總項數
12: Grid1.RecordCount = GetTotalCount();
13:
14: // 2.獲取當前分頁數據
15: DataTable table = GetPagedDataTable(Grid1.PageIndex, Grid1.PageSize);
16:
17: // 3.綁定到Grid
18: Grid1.DataSource = table;
19: Grid1.DataBind();
20: }
可以看出初始化表格數據需要如下幾個步驟:
- 設置表格的RecordCount屬性,告訴表格總記錄數;
- 根據表格的PageIndex(默認是0)和PageSize屬性,從數據庫查詢本頁的數據;
- 將查詢到的本頁數據綁定到表格。
由此可以看出,表格必須知道如下四種數據后,才能正確顯示:
- 當前是第幾頁(PageIndex);
- 總共有多少條數據(RecordCount);
- 每頁顯示多少條數據(PageSize);
- 當前頁的數據是什么(DataSource)。
再來看下分頁事件處理函數:
1: protected void Grid1_PageIndexChange(object sender, ExtAspNet.GridPageEventArgs e)
2: {
3: Grid1.PageIndex = e.NewPageIndex;
4:
5: BindGrid();
6: }
首先告訴表格接下來應該顯示哪一頁,然后重新查詢數據庫並綁定當前頁的數據。
界面顯示效果和內存分頁時一樣,就不再截圖。
為什么說內存分頁在大數據時性能差?
其實原因前面已經提到了,主要是因為內存分頁時會把所有表格數據保存到頁面的狀態視圖中(ViewState),導致頁面大小迅速增加,從而增加網絡下載上載的時間,並且減慢了頁面的渲染速度。
雖然ExtAspNet放棄了在ViewState中保存數據,從而可以在Ajax的環境中減少網絡的數據傳輸量,但是內存分頁時所有的表格數據還是要保存下來,供下次分頁時使用。
拿本篇文章中的內存分頁示例,在用戶點擊下一頁時,通過FireBug可以看到這次HTTP Post請求:
其中X_STATE就是ExtAspNet保存控件狀態的地方,把全部X_STATE的內容拷貝下來,並執行如下JavaScript代碼:
1: var xstate = JSON.parse(Base64.decode('eyJHcmlk...FtdXX19'));
2: var values = xstate.Grid1.X_Rows.Values;
3: values.length + '\r\n' + JSON.stringify(values);
可以看到如下的執行結果:
由此可見,全部的11條數據在每次頁面回發時都會作為HTTP Post的參數上傳到服務器,從而在大數據量的情況下導致頁面性能急劇下降。
小結
排序和分頁是表格的最基本操作,也是每個程序員都應該熟練掌握的知識。特別是處理大量數據(一般大於100條)一定要使用數據庫分頁,否則頁面性能會非常差。下一篇文章我們會討論ExtAspNet對表格的擴展列,包括序號列、選擇框列、行擴展列、模擬樹列以及彈出窗體列。