ListView + DataSource + 分頁功能與自訂查詢+排序
這次因為需求,使用自訂分頁,
而分頁的方式則是用SQL SERVER 2005 以上的Row_Number()函式。
首先先介紹前端的ListView要設定的功能
他會設定他的資料來源為ObjectDataSource,ID為OdsGrid
<asp:ListView ID="lvGrid" runat="server"
OnSorting="lvGrid_Sorting" DataSourceID ="OdsGrid" >
<LayoutTemplate>
<table id="Grid" runat="server" class="grid" cellpadding="0" cellspacing="0" style="empty-cells: show;">
<tr class="head">
<th style="width: 10%">
<asp:LinkButton ID="btnSortGRID_CODE" runat="server" CommandName="Sort" CommandArgument="GRID_CODE" Text="表格代碼" />
</th>
<th style="width: 10%">
<asp:LinkButton ID="btnSortGRID_NAME" runat="server" CommandName="Sort" CommandArgument="GRID_NAME" Text="表格名稱" />
</th>
</tr>
<tr id="itemPlaceholder" runat="server" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr id="row" runat="server" class='<%# Container.DataItemIndex % 2 == 0 ? "row" : "altrow" %>'>
<td>
<%# Eval("GridCode")%>
</td>
<td>
<%# Eval("GridName")%>
</td>
</tr>
</ItemTemplate>
</asp:ListView>
以下是DataPager的設定
<asp:DataPager ID="pagerTop" runat="server" PagedControlID ="lvGrid" PageSize ="10">
<Fields>
<asp:NextPreviousPagerField ShowFirstPageButton="true" ShowPreviousPageButton="true"
ShowLastPageButton="true" ShowNextPageButton="true"
FirstPageImageUrl="~/App_Themes/Theme1/img/icon-frew.gif"
LastPageImageUrl="~/App_Themes/Theme1/img/icon-ffwd.gif"
NextPageImageUrl="~/App_Themes/Theme1/img/icon-fwd.gif"
PreviousPageImageUrl="~/App_Themes/Theme1/img/icon-rew.gif"
ButtonCssClass="command" ButtonType="Image" />
<asp:TemplatePagerField>
<PagerTemplate>
<%--<td class="info">--%>
目前在第 <b>
<%# Container.TotalRowCount > 0 ? Math.Ceiling(((double)(Container.StartRowIndex + Container.MaximumRows) / Container.MaximumRows)) : 0 %>
</b>頁,共 <b>
<%# Math.Ceiling((double)Container.TotalRowCount / Container.MaximumRows)%>
</b>頁(共 <%# Container.TotalRowCount %> 筆)
<%--</td>--%>
</PagerTemplate>
</asp:TemplatePagerField>
</Fields>
</asp:DataPager>
而我查詢時,會傳Grid物件為查詢條件。因此ObjectDataSource要用下面方式設定
<asp:ObjectDataSource ID="OdsGrid" runat="server" SelectMethod="GetData"
TypeName="OdsGrid" SelectCountMethod="GetRowsCount"
StartRowIndexParameterName="rowIndex"
MaximumRowsParameterName="pageSize"
EnablePaging="True"
onselecting="OdsGrid_Selecting">
<SelectParameters >
<asp:Parameter Name ="sortExpression" Type ="String" />
<asp:Parameter Name ="objGrid" Type ="Object" />
</SelectParameters>
</asp:ObjectDataSource>
以下是上面設定的說明
TypeName:表示我提供Select方法的類別,ObjectDataSource其實是利用反射的機制,
動態的使用設定的物件,並使用該物件呼叫Method,使用反射去找出與設定的參數符合的方法名稱與參數名稱(所以參數名稱一定要個數相同並且名稱要相同、型態也要相同)
SelectMehotd:表示每次撈資料時會去呼叫的Method,這部分要小心的事,如果你有用到點上上方標題做排序的話,基本上應該要回傳的資料應該為DataView、DataTable之類的才能夠排序,但剛好我用的是回傳IList<T>,因此這解決排序Error的問題下面會再提到如何解決。
SelectCountMethod:為每次撈資料時他會呼叫這個,取得這次撈資料總數為多少,以搭配DataPager使用,才能計算分頁數。
StartRowIndexParameterName:為目前頁面開始的資料索引,
MaximumRowsParameterName:為一頁最大的資料個數,
上述兩個設定會傳至SelectMethod中,也就是GetData,就等於是呼叫GetData(rowIndex,pageSize)或你要這樣擺也可以GetData(pageSize,rowIndex),但參數名稱必需相同,由於他使用的是反射機制,Mapping不到,都會給你來個Error
EnablePaging:是否要分頁的意思,所以當然是True囉。
onSelecting:這是為了應付自行設定查詢參數而用的,在呼叫GetData前,替自訂的參數設定參數值,所以在此時會加入我自訂的內容,如查詢條件物件,排序等(因為我使用IList他不給我過,只好我在這邊自行加入排序條件囉!)
自訂Select參數<SelectParameters>…</SelectParameters>
我設定了兩個參數,sortExpression、objGrid,由於我要傳的是物件,所以objGrid的Type就設定為Object
因此這樣就有四個參數,所以程式會去尋找有四個參數的GetData的Method,並且四個參數名稱要相同(順序可以不同)
這樣我的SelectMethod就要長這樣了:GetData(int rowIndex,int pageSize,Grid objGridstring,string sortExpression)
再來就是介紹如何寫後端程式Cs
我有使用自訂排序方式,所以會使用自訂屬性使用ViewState
public string DefaultSortColumn = "GRID_CODE";
public string DefaultSortDirection = "ASC";
/// <summary>
/// 排序欄位
/// </summary>
public string vsSortColumn
{
set { this.ViewState["SortColumn"] = value; }
get { return ((string)(this.ViewState["SortColumn"] ?? DefaultSortColumn)); }
}
/// <summary>
/// 排序方向
/// </summary>
public string vsSortDirection
{
set { this.ViewState["SortDirection"] = value; }
get { return ((string)(this.ViewState["SortDirection"] ?? DefaultSortDirection)); }
}
在按查詢按鈕時,我會重新對ListView DataBind
protected void btnSearch_Click(object sender, EventArgs e)
{
this.lvGrid.DataBind();
}
在按了標題列排序時,我會記錄排序欄位與方向,並取消排序,重新DataBind,這樣我傳回值為IList<T>也能做到排序
//排序功能
protected void lvGrid_Sorting(object sender, ListViewSortEventArgs e)
{
//若與原先不相同,預設為遞增
if (this.vsSortColumn == e.SortExpression)
{
if (this.vsSortDirection == "ASC")
this.vsSortDirection = "DESC";
else
this.vsSortDirection = "ASC";
}
else
{
this.vsSortColumn = e.SortExpression;
this.vsSortDirection = "ASC";
}
e.Cancel = true;
this.lvGrid.DataBind();
}
在每次排序前,我會注入自訂查詢條件
protected void OdsGrid_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
string strSortExpression = "";
Grid objGrid = new Grid();
objGrid.GridCode = this.txtGridCode.Text;
strSortExpression = this.vsSortColumn + " " + this.vsSortDirection;
e.InputParameters["sortExpression"] = strSortExpression;
e.InputParameters["objGrid"] = objGrid;
}
接著要撰寫OdsGrid類別,
要撰寫GetData與GetRowsCount函式提供給ObjectDataSource使用
其中第十行,是使用特定的方式去得到資料的一個資料存取物件。
這部分的細節就不講了。(總之他是一個DAL物件)
/// <summary>
/// Grid ObjectDataSource
/// </summary>
public class OdsGrid
{
IQueryService objQueryService;
public OdsGrid()
{
objQueryService = (IQueryService)Core.WebUtility.Repository.QueryService();
}
/// <summary>
/// 取得資料來源
/// </summary>
/// <param name="rowIndex">Index of the row.</param>
/// <param name="pageSize">Size of the page.</param>
/// <param name="objGrid">The obj Grid.</param>
/// <param name="sortExpression">The sort expression.</param>
/// <returns>objList</returns>
public IList<Grid> GetData(int rowIndex, int pageSize,Grid objGrid, string sortExpression)
{
int PageIndex = rowIndex / pageSize;
int RowsCount; //資料總數
int TotalPage; //資料總頁數
int CurrentPage; //目前頁數(1起跳)
int StartRowNo; //存取起始資料號碼
int EndRowNo; //存取結束資料號碼
RowsCount = this.GetRowsCount(objGrid);
CurrentPage = PageIndex+1;
//取得總頁數
if (RowsCount % pageSize == 0)
TotalPage = RowsCount / pageSize;
else
TotalPage = RowsCount / pageSize + 1;
//目前頁數大於總頁數時,目前頁數設定為總頁數
if (CurrentPage > TotalPage)
CurrentPage = TotalPage;
EndRowNo = CurrentPage * pageSize;
StartRowNo = (CurrentPage - 1) * pageSize + 1;
//若結束資料數字大於總資料數字,結束資料數字設定為總資料數字
if (EndRowNo > RowsCount)
{
EndRowNo = RowsCount;
}
return this.objQueryService.GetGridListByCondition(StartRowNo, EndRowNo, objGrid, sortExpression);
}
/// <summary>
/// 取得資料來源的總筆數,依條件得到筆數
/// </summary>
/// <param name="objGrid">The obj Grid.</param>
/// <returns>資料來源總筆數</returns>
public int GetRowsCount(Grid objGrid)
{
return this.GetRowsCount("", objGrid);
}
/// <summary>
/// 取得資料來源的總筆數,依條件得到筆數
/// </summary>
/// <param name="sortExpression">The sort expression.</param>
/// <param name="objGrid">The obj Grid.</param>
/// <returns>資料來源總筆數</returns>
public int GetRowsCount(string sortExpression, Grid objGrid)
{
return this.objQueryService.GetGridCountByCondtion(objGrid)) ;
}
}
而GetGridCount及GetGridListByCondition的函式裡面使用的SQL語法
就只是Select Count(*) From Table
及
sql = SELECT * ,ROW_NUMBER() OVER (ORDER BY " + sortExpression + @") as RowNo
FROM GRID AS GRID(Nolock)
sql = " Select * From ( "+sql+") As NewTable ";
sql += " WHERE NewTable.RowNo >= “+StartRowNo+” AND NewTable.RowNo <= “ + EndRowNo ;
sql += " ORDER BY " + sortExpression;
其實過程艱辛萬苦,東抄西抄,只抄到70%~80%的Code,因為少了一些特殊的做法,
我就當筆記記下來,
記錄一下很少搜尋到的做法,像自訂查詢參數該怎麼做、如何自訂排序+後面自己要下SQL自訂分頁的這些作法。