舉凡一般的表格顯示會優先考慮GridView,而列表顯示會有Repeater, DataList,
雖然知道有ListView可以用,但一直沒有去試著使用。
如果遇到分頁呢?如果是用GridView,我是不會去使用GridView的分頁功能,
我都是用ADO.NET以及自定的一個Pager使用者自定控制項,
坦白說,有時候這樣的分頁方式做久了都會覺得煩也覺得有些 stupid …
這篇文章就只是純粹記錄一下使用ListView+DataPager+LinqDataSource的基礎用法。
需要達到以下的需求:
1. 顯示產品資料,預設顯示所有產品資料。
2. 產品分類使用下拉選單,並且下拉選單變換選項時,顯示選定分類的所有產品。
3. 分頁功能,產品分類選項改變後,也需要有分頁顯示
4. 由分類的某分頁去變換下拉選單選項後,到選定分類產品資料顯示時,如有分頁,需從第一頁開始顯示
目前頁面是在分類「Beverages」(CategoryID = 1),下拉選單選定分類「Grains/Cereals」
改變分類後,該分類有分頁顯示,從第一頁開始顯示
Step 1.
使用Linq to SQL,用 Northwind 建立dbml,這次只有使用兩個Table來做演練,
Categories, Products
Step 2.
先將分類的下拉選單做出來
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: }Step 3.
拉一個LinqDataSource到設計頁面上
資料表的選項要改為「Products」
選擇上圖的「Where」,要加入篩選的條件,要使用頁面上的分類下拉選單控制項來變換資料的內容
資料行選擇「Products」的欄位列出
參數屬性中的預設值不用填入任何值
設定好選項之後,按下「加入」後再按「確定」以完成Where模式的設定
回到設定資料來源畫面後,接下來設定「OrderBy」
設定好排序的欄位與排序的方式
選好後按下確定
完成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: }最後完成的畫面
資料顯示
分頁
分類選擇
SQL 觀察
是否會像GridView的分頁那樣呢?(將所有的資料一次全部撈回來之後再顯示分頁的資料,很不明智也耗效能)
我們觀察SQL Server Profiler
一開始的初始狀態
詳細的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 + @p19: ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=0,@p1=5
可以看到由Linq所產生的SQL Statement有使用Row_Number()函式,然後取得分頁量的資料
我們試著變換分類而且也換頁來看看
詳細的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 + @p210: 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動態綁定資料來源時的分頁問題以上
沒有留言:
張貼留言