2010年9月1日 星期三

ListView + DataPager + LinqDataSource

說來慚愧,因為之前所經歷的專案中都沒有使用過「ListView + DataPager + LinqDataSource」
舉凡一般的表格顯示會優先考慮GridView,而列表顯示會有Repeater, DataList,
雖然知道有ListView可以用,但一直沒有去試著使用。




如果遇到分頁呢?如果是用GridView,我是不會去使用GridView的分頁功能,
我都是用ADO.NET以及自定的一個Pager使用者自定控制項,
坦白說,有時候這樣的分頁方式做久了都會覺得煩也覺得有些 stupid …

這篇文章就只是純粹記錄一下使用ListView+DataPager+LinqDataSource的基礎用法。

需要達到以下的需求:
1. 顯示產品資料,預設顯示所有產品資料。
image
2. 產品分類使用下拉選單,並且下拉選單變換選項時,顯示選定分類的所有產品。
image
3. 分頁功能,產品分類選項改變後,也需要有分頁顯示
image
4. 由分類的某分頁去變換下拉選單選項後,到選定分類產品資料顯示時,如有分頁,需從第一頁開始顯示
目前頁面是在分類「Beverages」(CategoryID = 1),下拉選單選定分類「Grains/Cereals」
image
改變分類後,該分類有分頁顯示,從第一頁開始顯示
image

Step 1.
使用Linq to SQL,用 Northwind 建立dbml,這次只有使用兩個Table來做演練,
Categories, Products
image

Step 2.
先將分類的下拉選單做出來
image
   1: Category :
   2: <asp:DropDownList ID="ddlCategories" runat="server" AutoPostBack="True">
   3: </asp:DropDownList>

Code Behind
   1: NorthwindDataContext db = new NorthwindDataContext();
   2:  
   3: protected void Page_Load(object sender, EventArgs e)
   4: {
   5:     if (!Page.IsPostBack)
   6:     {
   7:         DropDownListDataBind();
   8:     }
   9: }
  10:  
  11: private void DropDownListDataBind()
  12: {
  13:     var categories = db.Categories.OrderBy(c => c.CategoryID);
  14:  
  15:     List<ListItem> items = new List<ListItem>();
  16:     foreach (var item in categories)
  17:     {
  18:         items.Add(new ListItem(item.CategoryName, item.CategoryID.ToString()));
  19:     }
  20:     this.ddlCategories.Items.AddRange(items.ToArray());
  21:     this.ddlCategories.Items.Insert(0, new ListItem("-- select a Category --", string.Empty));
  22: }
結果顯示
image



Step 3.

拉一個LinqDataSource到設計頁面上

image



資料表的選項要改為「Products」

image

選擇上圖的「Where」,要加入篩選的條件,要使用頁面上的分類下拉選單控制項來變換資料的內容

資料行選擇「Products」的欄位列出

參數屬性中的預設值不用填入任何值

image



設定好選項之後,按下「加入」後再按「確定」以完成Where模式的設定

image

回到設定資料來源畫面後,接下來設定「OrderBy」

image

設定好排序的欄位與排序的方式

選好後按下確定

image

完成LinqDataSource的設定

產生的原始碼內容如下,但是要做個小修改
   1: <asp:LinqDataSource ID="LinqDataSource1" runat="server" 
   2:     ContextTypeName="NorthwindDataContext" EntityTypeName="" 
   3:     OrderBy="CategoryID, ProductID" TableName="Products" 
   4:     Where="CategoryID == @CategoryID">
   5:     <WhereParameters>
   6:         <asp:ControlParameter ControlID="ddlCategories" Name="CategoryID" 
   7:             PropertyName="SelectedValue" Type="Int32" />
   8:     </WhereParameters>
   9: </asp:LinqDataSource>

修改後,加上AutoGenerateWhereClause並刪除Where,


   1: <asp:LinqDataSource ID="LinqDataSource1" runat="server" 
   2:     ContextTypeName="NorthwindDataContext" EntityTypeName="" 
   3:     OrderBy="CategoryID, ProductID" TableName="Products" 
   4:     AutoGenerateWhereClause="true">
   5:     <WhereParameters>
   6:         <asp:ControlParameter ControlID="ddlCategories" Name="CategoryID" 
   7:             PropertyName="SelectedValue" Type="Int32" />
   8:     </WhereParameters>
   9: </asp:LinqDataSource>

Step 4.

加入 ListView與 DataPager
   1: <asp:ListView ID="ListView1" runat="server" DataSourceID="LinqDataSource1" 
   2:     onselectedindexchanged="ListView1_SelectedIndexChanged">
   3:     <LayoutTemplate>
   4:             <ul ID="itemPlaceholderContainer" runat="server" style="border-bottom: 1px #bfbfbf solid; padding-bottom: 1px;">
   5:                 <li ID="itemPlaceholder" runat="server" />
   6:             </ul>
   7:             <div class="pagination">
   8:                 <asp:DataPager ID="DataPager1" runat="server" PageSize="5">
   9:                         <Fields>
  10:                             <asp:NextPreviousPagerField ButtonCssClass="Previous" ShowNextPageButton="false" PreviousPageText="Previous" /> 
  11:                             <asp:NumericPagerField />
  12:                             <asp:NextPreviousPagerField ShowPreviousPageButton="false" ButtonCssClass="Next" NextPageText="Next" />
  13:                         </Fields>
  14:                 </asp:DataPager>
  15:             </div>
  16:  
  17:     </LayoutTemplate>
  18:     <ItemTemplate>
  19:             <li class="ListContent">
  20:                 <span style="font-weight: bold"> CategoryID : <%# Eval("CategoryID") %></span>
  21:                 <br />
  22:                 ProductId : <%# Eval("ProductID") %>
  23:                 <br />
  24:                 Name : <%# Eval("ProductName") %>
  25:                 <br />
  26:                 QuantityPerUnit : <%# Eval("QuantityPerUnit") %>
  27:                 <br />
  28:                 UnitPrice : <%# Eval("UnitPrice") %>
  29:             </li>
  30:     </ItemTemplate>
  31: </asp:ListView>

Code Behind

處理分頁,要對DataPager的StartRowIndex做判斷設定,

這是因為當有某分類的產品數小於分頁量(PageSize)時並需將StartRowIndex設定為 0 (第一頁)
   1: protected void ListView1_SelectedIndexChanged(object sender, EventArgs e)
   2: {
   3:     DataPager dataPager = this.ListView1.FindControl("DataPager1") as DataPager;
   4:     
   5:     int StartRowIndex = dataPager.TotalRowCount < dataPager.PageSize
   6:          ? 0
   7:          : dataPager.StartRowIndex;
   8:  
   9:     dataPager.SetPageProperties(StartRowIndex, dataPager.PageSize, true);
  10: }

Step 5.

接下來處理改變分類後,需要將分頁歸到該分類的第一頁,

這個動作就要在DropDownList的SelectedIndexChanged事件中

Code Behind 1:先增加欄位與屬性用來存放分類的ID
   1: private int _CategoryID;
   2: public int CategoryID
   3: {
   4:     get { return _CategoryID; }
   5:     set { _CategoryID = value; }
   6: }

Code Behind 2:
   1: protected void ddlCategories_SelectedIndexChanged(object sender, EventArgs e)
   2: {
   3:     DataPager dataPager = this.ListView1.FindControl("DataPager1") as DataPager;
   4:  
   5:     if (string.IsNullOrEmpty(this.ddlCategories.SelectedValue))
   6:     {
   7:         dataPager.SetPageProperties(0, dataPager.PageSize, true);
   8:     }
   9:     else
  10:     {
  11:         if (!this.CategoryID.ToString().Equals(this.ddlCategories.SelectedValue))
  12:         {
  13:             dataPager.SetPageProperties(0, dataPager.PageSize, true);
  14:         }
  15:         this.CategoryID = Convert.ToInt32(this.ddlCategories.SelectedValue);
  16:     }
  17: }

最後完成的畫面

資料顯示

image

分頁

image

分類選擇

image



SQL 觀察

是否會像GridView的分頁那樣呢?(將所有的資料一次全部撈回來之後再顯示分頁的資料,很不明智也耗效能)

我們觀察SQL Server Profiler

一開始的初始狀態

image

詳細的SQL Statement


   1: exec sp_executesql N'SELECT [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], 
   2: [t1].[Discontinued]
   3: FROM (
   4:     SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ProductID]) AS [ROW_NUMBER], [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], 
   5: [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
   6:     FROM [dbo].[Products] AS [t0]
   7:     ) AS [t1]
   8: WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
   9: ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=0,@p1=5

可以看到由Linq所產生的SQL Statement有使用Row_Number()函式,然後取得分頁量的資料

我們試著變換分類而且也換頁來看看

image

詳細的SQL Statement


   1: exec sp_executesql N'SELECT [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], 
   2: [t1].[Discontinued]
   3: FROM (
   4:     SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ProductID]) AS [ROW_NUMBER], [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], 
   5: [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
   6:     FROM [dbo].[Products] AS [t0]
   7:     WHERE [t0].[CategoryID] = @p0
   8:     ) AS [t1]
   9: WHERE [t1].[ROW_NUMBER] BETWEEN @p1 + 1 AND @p1 + @p2
  10: ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int,@p2 int',@p0=1,@p1=10,@p2=5

分類編號 @p0 = 1

分頁起始RowInde @p1=10

分頁量 @p2=5



由以上的觀察可以看出,ListView + DataPager + LinqDataSource是可以達成真正的分頁功能,

程式開人員可以擺脫過去要自己處理分頁的許多複雜事件處理。



相關連結:

範例資料庫

下載 Northwind 和 pubs 範例資料庫


Will 保哥

超完美組合:LinqDataSource + ListView + DataPager + jQuery

ListView + DataPager 在不使用 LinqDataSource 時會有問題


RandomHsu (RandomArt.tw)
ListView分頁控制項的使用方法(DataPager)
ListView動態綁定資料來源時的分頁問題

以上





沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。