ListView + DataSource + 分頁功能與自訂查詢+排序

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自訂分頁的這些作法。