WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

ASP.NET MVC でページング (CORE 版)

by WebSurfer 30. January 2021 14:45

Web アプリでデータベースのレコード一覧を表示する場合、レコード数が多い場合はページングが必要になってくると思いますが、ASP.NET Core MVC でそれをどのように実装するかという話を書きます。

.NET Frammwork 版の MVC5 アプリでのページングの実装方法は Microsoft のドキュメント「チュートリアル: ASP.NET MVC アプリケーションでの Entity Framework を使用した並べ替え、フィルター処理、およびページングの追加」に書かれています。NuGet から PagedList パッケージをインストールしてそれを利用するというものです。

Core 版の MVC アプリではどうすればよいかですが、これも Microsoft のドキュメント「チュートリアル: 並べ替え、フィルター処理、ページングを追加する - ASP.NET MVC と EF Core」に説明がありました。

というわけで、そのチュートリアルを見ればこの記事の話は終わりなのですが、それではブログの記事としては面白くないので、ページャーの機能を拡張し、Microsoft のドキュメントにはページャーとして Previous と Next の機能しかなかったものに、上の画像のように First と Last およびページ番号のリンクを追加しました。

上の画像を表示したサンプルコードは以下の通りです。表示するのを Microsoft のサンプルデータベース Northwind の Products テーブルとした点とページャー機能の拡張以外は Microsoft のチュートリアルと同じです。

サンプルは .NET 5.0 ベースで作りましたが、肝心な部分は .NET Core 3.1 でも同じだと思います。(.NET 5.0 ベースにしたのは EF Core 5.0 で導入されたシンプルなログで SQL Server に渡される SQL 文を見ることができるという理由です)

コンテキストクラス、エンティティクラス

Microsoft のサンプルデータベース Northwind からリバースエンジニアリングで生成したものを利用しました。ここでの話にはほとんど関係ないのですが、参考までに自動生成されたエンティティクラス Product のコードを以下にアップしておきます。

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

#nullable disable

namespace MvcCore5App.Models
{
    [Index(nameof(CategoryId), Name = "CategoriesProducts")]
    [Index(nameof(CategoryId), Name = "CategoryID")]
    [Index(nameof(ProductName), Name = "ProductName")]
    [Index(nameof(SupplierId), Name = "SupplierID")]
    [Index(nameof(SupplierId), Name = "SuppliersProducts")]
    public partial class Product
    {
        [Key]
        [Column("ProductID")]
        public int ProductId { get; set; }
        [Required]
        [StringLength(40)]
        public string ProductName { get; set; }
        [Column("SupplierID")]
        public int? SupplierId { get; set; }
        [Column("CategoryID")]
        public int? CategoryId { get; set; }
        [StringLength(20)]
        public string QuantityPerUnit { get; set; }
        [Column(TypeName = "money")]
        public decimal? UnitPrice { get; set; }
        public short? UnitsInStock { get; set; }
        public short? UnitsOnOrder { get; set; }
        public short? ReorderLevel { get; set; }
        public bool Discontinued { get; set; }

        [ForeignKey(nameof(CategoryId))]
        [InverseProperty("Products")]
        public virtual Category Category { get; set; }
        [ForeignKey(nameof(SupplierId))]
        [InverseProperty("Products")]
        public virtual Supplier Supplier { get; set; }
    }
}

PaginatedList.cs

Microsoft のチュートリアルのコードと全く同じですが、少しコメントを加えて以下にアップしておきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace MvcCore5App.Services
{
    public class PaginatedList<T> : List<T>
    {
        // 表示するページのページ番号
        public int PageIndex { get; private set; }

        // ページ総数
        public int TotalPages { get; private set; }

        // コンストラクタ。下の CreateAsync メソッドから呼ばれる
        public PaginatedList(List<T> items, 
                             int count, 
                             int pageIndex, 
                             int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);

            this.AddRange(items);
        }

        // 表示するページの前にページがあるか?
        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        // 表示するページの後にページがあるか?
        public bool HasNextPage
        {
            get
            {
                return (PageIndex < TotalPages);
            }
        }

        // 下の静的メソッドがコントローラーから呼ばれて戻り値がモデルとして
        // ビューに渡される。引数の pageSize は 1 ページに表示するレコード
        // 数でコントローラーから渡される
        public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, 
                                                               int pageIndex, 
                                                               int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip((pageIndex - 1) * pageSize).
                                     Take(pageSize).
                                     ToListAsync();

            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}

このクラスがページングの中枢を担うもので、コントローラーが並べ替え、フィルター処理を追加した後の IQueryable<Product> オブジェクトを CreateAsync メソッドが受け取って、Skip, Take メソッドでページに表示する部分のみをデータベースから取得し、PaginatedList<Product> オブジェクトを生成して戻り値として返すようにしています。

上の Linq to Entities で使われている Skip, Take メソッドですが、全レコードを取得した後でページに表示するレコードのみを切り取るのかと思っていましたがそうではなく、SQL Server には以下のような SQL 文が渡されてました。

SELECT p.ProductID, ...
FROM Products AS p
ORDER BY p.ProductID ASC
OFFSET {Skip 数} ROWS
FETCH NEXT {Take 数} ROWS ONLY

Web Forms アプリの SqlDataSource + GridView とかですと全レコード取得後に表示する部分を切り取るということになるのですが、そのあたりは進化しているようです。

Comtroller / Action Method

データの取得先を Microsoft のサンプルデータベース Northwind の Products テーブルとした点以外はチュートリアルのコードと同じです。

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using MvcCore5App.DAL;
using MvcCore5App.Models;
using MvcCore5App.Services;

namespace MvcCore5App.Controllers
{
    public class ProductsController : Controller
    {
        private readonly NorthwindContext _context;

        public ProductsController(NorthwindContext context)
        {
            _context = context;
        }

        public async Task<IActionResult> PagedIndex(string sortOrder, 
                                                    string currentFilter, 
                                                    string searchString,
                                                    int? pageNumber)
        {
            ViewData["CurrentSort"] = sortOrder;
            ViewData["NameSortParm"] = 
                String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewData["PriceSortParm"] = 
                sortOrder == "Price" ? "price_desc" : "Price";

            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }

            ViewData["CurrentFilter"] = searchString;

            var products = from p in _context.Products.Include(p => p.Category)
                           select p;

            if (!String.IsNullOrEmpty(searchString))
            {
                products = products.Where(p => p.ProductName.Contains(searchString));
            }

            switch (sortOrder)
            {
                case "name_desc":
                    products = products.OrderByDescending(p => p.ProductName);
                    break;
                case "Price":
                    products = products.OrderBy(p => p.UnitPrice);
                    break;
                case "price_desc":
                    products = products.OrderByDescending(p => p.UnitPrice);
                    break;
                default:
                    products = products.OrderBy(p => p.ProductName);
                    break;
            }

            int pageSize = 5;   // 1 ページに表示するレコードの数

            return View(await PaginatedList<Product>.CreateAsync(products.AsNoTracking(), 
                                                                 pageNumber ?? 1, 
                                                                 pageSize));
        }
    }
}

View

ページャーの機能を拡張し、Microsoft のドキュメントにあった Previous と Next に、First と Last およびページ番号のリンクを追加しました。結果は上の画像のようになります。

@model MvcCore5App.Services.PaginatedList<Product>

@{
    ViewData["Title"] = "PagedIndex";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>PagedIndex</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>

<form asp-action="PagedIndex" method="get">
    <div class="form-actions no-color">
        <p>
            Find by Product Name: 
            <input type="text" name="SearchString" 
                   value="@ViewData["CurrentFilter"]" />
            <input type="submit" value="Search" class="btn btn-primary" /> |
            <a asp-action="PagedIndex">Back to Full List</a>
        </p>
    </div>
</form>

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-action="PagedIndex" 
                   asp-route-sortOrder="@ViewData["NameSortParm"]"
                   asp-route-currentFilter="@ViewData["CurrentFilter"]">
                    Product Name
                </a>
            </th>
            <th>
                Category
            </th>
            <th>
                <a asp-action="PagedIndex" 
                   asp-route-sortOrder="@ViewData["PriceSortParm"]"
                   asp-route-currentFilter="@ViewData["CurrentFilter"]">
                    Unit Price
                </a>
            </th>
            <th>
                Discontinued
            </th>

            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.ProductName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Category.CategoryName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.UnitPrice)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Discontinued)
                </td>

                <td>
                    <a asp-action="Edit" asp-route-id="@item.ProductId">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.ProductId">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.ProductId">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

@{
    var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}

<span>Page @Model.PageIndex of @Model.TotalPages</span>
<br />
<a asp-action="PagedIndex"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="1"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-secondary @prevDisabled">
    First
</a>
<a asp-action="PagedIndex"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex - 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-secondary @prevDisabled">
    Prev
</a>

@{
    // ページ総数が 5 を超える場合は Prev と Next の間に 1 2 3 4 5 と
    // いうようなリンクを表示し、その番号のページに飛べるようにした
    if (Model.TotalPages > 5)
    {
        if (Model.PageIndex <= 3)
        {
            for (int i = 1; i <= 5; i++)
            {
                var disabled = (Model.PageIndex == i) ? "disabled" : "";
                <a asp-action="PagedIndex"
                   asp-route-sortOrder="@ViewData["CurrentSort"]"
                   asp-route-pageNumber="@i"
                   asp-route-currentFilter="@ViewData["CurrentFilter"]"
                   class="btn btn-secondary @disabled">
                    @i
                </a>
            }
        }
        else if (Model.PageIndex < (Model.TotalPages - 2))
        {
            for (int i = Model.PageIndex - 2; i <= Model.PageIndex + 2; i++)
            {
                var disabled = (Model.PageIndex == i) ? "disabled" : "";
                <a asp-action="PagedIndex"
                   asp-route-sortOrder="@ViewData["CurrentSort"]"
                   asp-route-pageNumber="@i"
                   asp-route-currentFilter="@ViewData["CurrentFilter"]"
                   class="btn btn-secondary @disabled">
                    @i
                </a>
            }
        }
        else
        {
            for (int i = Model.TotalPages - 4; i <= Model.TotalPages; i++)
            {
                var disabled = (Model.PageIndex == i) ? "disabled" : "";
                <a asp-action="PagedIndex"
                   asp-route-sortOrder="@ViewData["CurrentSort"]"
                   asp-route-pageNumber="@i"
                   asp-route-currentFilter="@ViewData["CurrentFilter"]"
                   class="btn btn-secondary @disabled">
                    @i
                </a>
            }
        }
    }
}

<a asp-action="PagedIndex"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.PageIndex + 1)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-secondary @nextDisabled">
    Next
</a>
<a asp-action="PagedIndex"
   asp-route-sortOrder="@ViewData["CurrentSort"]"
   asp-route-pageNumber="@(Model.TotalPages)"
   asp-route-currentFilter="@ViewData["CurrentFilter"]"
   class="btn btn-secondary @nextDisabled">
    Last
</a>

まず、最初のページおよび最後のページに飛べるよう First, Last というリンクを追加しました。

さらに、コード内のコメントにも書きましたように、ページ総数が 5 を超える場合は Prev と Next の間に 1 2 3 4 5 というようなページ番号のリンクを表示し、それをクリックすることによりその番号のページに飛べるようにしてみました。

ページング機能は Html ヘルパーにまとめ、全ページ数が 5 を超える場合の 5 という数字は任意の数を外部から設定できるようにした方が良さそうですが、それは今後の検討課題ということで・・・

Tags: , ,

Paging

GridView の Pager に総レコード数を表示

by WebSurfer 6. December 2014 18:26

以下の画像のように、ページングが有効になっている GridView の Pager 内に「総レコード数」を表示するにはどうしたらいいかということを書きます。

Pager に総レコード数を表示

ListView と DataPager を利用する場合は、先の記事 ListView でページ指定 のサンプルコードのように Container.TotalRowCount で総レコード数が取得できます。(注:Container は DataPager オブジェクトを参照しています)

しかしながら、GirdView を利用した場合、ページング機能は GirdView に統合されているものを利用することになり、それから「総レコード数」を取得するプロパティなどはありません。

GridView は、データーソースコントロール(SqlDataSource または ObjectDataSource)と組み合わせて使うケースがほとんどだと思いますが、その場合はデーターソースコントロールの Selected イベントのハンドラを使って「総レコード数」を取得するのが簡単そうです。

例えば、データーソースコントロールに SqlDataSource を使う場合、その Selected イベントのハンドラの引数 SqlDataSourceStatusEventArgs クラスAffectedRows プロパティ から総レコード数を取得できます。

取得した「総レコード数」をページャーの中に表示するには、GridView.RowDataBound イベント のハンドラでページャーの行を探して、その中に追加します。

ページャーそのものはデフォルトで(何も設定しなくても) 1 2 3 4 5 6 7 8 9 10 ... というようなクリックするとその番号のページに飛べるハイパーリンクが生成されますが、PagerSettings クラス を使うともう少し細かな設定ができます。

上の画像のページャーは、PagerSettings クラスの Mode プロパティを NumericFirstLast に、PageButtonCount プロパティを 5 に設定した場合のものです。

SqlDataSource と GridView を使った場合のサンプル(上の画像のページを表示したもの)を以下にアップしておきます。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  int GridView3TotalCount = 0;
    
  protected void SqlDataSource1_Selected(
        object sender, SqlDataSourceStatusEventArgs e)
  {
      GridView3TotalCount = e.AffectedRows;
  }

  protected void GridView3_RowDataBound(
        object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.Pager)
    {
      Label label = new Label();
      label.Text = "総レコード数: " + 
            GridView3TotalCount.ToString();
      e.Row.Cells[0].Controls.AddAt(0, label);
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>GridView Total Records</title>
</head>
<body>
  <form id="form1" runat="server">
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand="SELECT * FROM [Orders]" 
      OnSelected="SqlDataSource1_Selected">
    </asp:SqlDataSource>

    総レコード数: <%=GridView3TotalCount%>
    <asp:GridView ID="GridView3" runat="server" 
      AllowPaging="True" 
      AutoGenerateColumns="False" 
      DataKeyNames="OrderID" 
      DataSourceID="SqlDataSource1" 
      OnRowDataBound="GridView3_RowDataBound">
      <PagerSettings 
        Mode="NumericFirstLast" 
        PageButtonCount="5">
      </PagerSettings>
      <Columns>
        <asp:BoundField DataField="OrderID" 
          HeaderText="OrderID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="OrderID" />
        <asp:BoundField DataField="CustomerID" 
          HeaderText="CustomerID" 
          SortExpression="CustomerID" />
        <asp:BoundField DataField="OrderDate" 
          HeaderText="OrderDate" 
          SortExpression="OrderDate" 
          DataFormatString="{0:yyyy/MM/dd}" />
        <asp:BoundField DataField="Freight" 
          HeaderText="Freight" 
          SortExpression="Freight" 
          DataFormatString="${0:N2}" />
        <asp:BoundField DataField="ShipCountry" 
          HeaderText="ShipCountry" 
          SortExpression="ShipCountry" />
      </Columns>
    </asp:GridView>
  </form>
</body>
</html>

データーソースコントロールに ObjectDataSource を使う場合も、ObjectDataSource.Selected イベントのハンドラ で「総レコード数」を取得できます。

ObjectDataSource を使う場合、ページを切り替えるたびに全レコードを DB から再度取得するという無駄なことを避けるため、普通は、先の記事 ObjectDataSource でページング に書いてあるように、必要なレコードのみ DB から取得するクラス/メソッドを実装して、それらを ObjectDataSource で定義されているプロパティに設定することになると思います。

上に紹介した記事のサンプルコードでは、GetDataByIndex と GetNumberOfMessages の 2 つのメソッドが ObjectDataSource に設定されていますが、その両方が呼ばれ、それぞれで Selected イベントが発生します(2 回 Selected イベントが発生します)。

その際、イベントハンドラの引数 ObjectDataSourceStatusEventArgs の ReturnValue プロパティ でそれぞれのメソッドが返すオブジェクトを取���できます。

したがって、e.ReturnValue が Int32 型にキャストできれば GetNumberOfMessages が返したもの、即ち全レコード数ということになります。なので、イベントハンドラのコードを以下のようにすれば「総レコード数」を取得できます。

protected void ObjectDataSource1_Selected(object sender, 
                        ObjectDataSourceStatusEventArgs e)
{
  if (e.ReturnValue is Int32)
  {
    GridView3TotalCount = (Int32)e.ReturnValue;
  }
}

「総レコード数」さえ取得できれば、あとは GridView と Pager の実装を SqlDataSource を使った場合と同様にして Pager に表示できます。

Tags: ,

Paging

ObjectDataSource でページング

by WebSurfer 26. August 2010 12:00

DB のレコード一覧を表示する場合、レコード数が多い場合はページングが必要になってきます。

SqlDataSource を使って、ウィザードベースでクエリなどを設定していけば、コードを一行も書くことなく簡単にページング機能を実装できます。

しかしながら、ウィザードベースで実装した場合は、ページを切り替えるたびに全レコードを取得し、そのページの当該部分のみを表示するという動作になります。

レコード数が多い場合、ページングの都度全レコードを取得するのは負荷が大きく、できれば避けたい操作です。

ちなみに、先の記事 カスタムページャー で紹介した Repeater とカスタムページャーを使用したページングではそのあたりは考慮してあります。

今回は、先の記事と同等な内容を、ObjectDataSource と ListView を使って実現する例を書いてみます。実行結果は以下のようになります。

ListVie でのページング

ポイントは、先の記事のようなストアドプロシージャを使うのではなくて、必要なレコードのみ DB から取得するクラス/メソッドを実装して、それらを ObjectDataSource で定義されているプロパティに設定するというところです。

加えて、出来るだけ ObjectDataSource と ListView が持つページングの機能を利用し、自力でコードは書かない(上記のクラス/メソッドを除く)ということもあります。

まず、型付 DataSet + TableAdapter(xsd ファイル)を Visual Studio のウィザードを利用して作成します。

Microsoft が提供しているサンプルデータベース Northwind の Products テーブルを利用します。ウィザードに従って進めていくと xsd ファイルが作られ、これをベースに 型付 DataSet + TableAdapter のコードが自動生成されます。

xsd ファイルを開くと、以下のように表示されるはずです。

xsd ファイルの作成

自動生成されたコードは、Web アプリケーションプロジェクトの場合は xsd ファイルの直下に、Web サイトプロジェクトの場合は Temporary ASP.NET Files フォルダ内にあります。

この TableAdapter コードを partial class を使って拡張して、必要なレコードのみ DB から取得するためのクラス/メソッドを実装します。

以下のようになります。GetDataByIndex メソッドの引数名 startRowIndex, maximumRowsは、ObjectDataSource がデフォルトでその名前を参照していますので、そのまま使うのが面倒がないです(変更する場合は MaximumRowsParameterName プロパティと StartRowIndexParameterName プロパティに変更後の引数名を設定してください)。名前空間名、クラス名は、自動生成されたコードを参照してください。

using System;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using System.ComponentModel;

namespace ProductsDataSetTableAdapters
{
  public partial class ProductsTableAdapter
  {
    [DataObjectMethod(DataObjectMethodType.Select, true)]
    public ProductsDataSet.ProductsDataTable GetDataByIndex(int startRowIndex, int maximumRows)
    {
      SqlDataAdapter adapter;
      string query = String.Format(
        "SELECT * " +
          "FROM (" + 
             "SELECT *, ROW_NUMBER() OVER (ORDER BY [ProductID] ASC) AS rownum " + 
             "FROM [Products]) AS DerivedTable " +
          "WHERE rownum BETWEEN {0} AND {1} " +
          "ORDER BY [ProductID] ASC",
           startRowIndex + 1, maximumRows + startRowIndex);
      adapter = new SqlDataAdapter(query, this.Connection);

      ProductsDataSet dataset = new ProductsDataSet();
      adapter.Fill(dataset.Products);
      return dataset.Products;
    }

    public int GetNumberOfMessages()
    {
      SqlCommand command;
      command = 
        new SqlCommand("SELECT COUNT(*) FROM [Products]", this.Connection);
      this.Connection.Open();
      int rows = (int)command.ExecuteScalar();
      this.Connection.Close();
      return rows;
    }
  }
}

最後に、ObjectDataSource と ListView を実装した aspx ページを作成します。コードは以下のようになります。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <style type="text/css">
    table.style1
    {
      border-style: solid;
      border-width: 2px;
      border-color: Black;
      text-align: center;
      border-collapse: collapse;
    }
       
    table.style1 th
    {
      border-style: solid;
      border-width: 2px 1px 2px 1px;
      border-color: Black;
      background-color: #6699FF;
      color: #FFFFFF;
    }
        
    table.style1 td
    {
      border-style: solid;
      border-width: 1px;
      border-color: Black;        
    }
        
    .alternate
    {
      background-color: #CCFFFF;
    }  
  </style>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:ObjectDataSource ID="ObjectDataSource1" 
      runat="server" 
      SelectMethod="GetDataByIndex" 
      SelectCountMethod="GetNumberOfMessages" 
      EnablePaging="True"             
      TypeName="ProductsDataSetTableAdapters.ProductsTableAdapter">
    </asp:ObjectDataSource>
    <asp:ListView ID="ListView1" 
      runat="server" 
      DataKeyNames="ProductID" 
      DataSourceID="ObjectDataSource1" 
      EnableModelValidation="True">
      <LayoutTemplate>
        <table id="Table1" runat="server" class="style1">
          <tr id="Tr1" runat="server" style="">
            <th id="Th1" rowspan="2" runat="server">ProductID</th>
            <th id="Th2" colspan="7" runat="server">ProductName</th>
            <th id="Th3" rowspan="2" runat="server">Discontinued</th>
          </tr>
          <tr>
            <th id="Th4" runat="server">SupplierID</th>
            <th id="Th5" runat="server">CategoryID</th>
            <th id="Th6" runat="server">QuantityPerUnit</th>
            <th id="Th7" runat="server">UnitPrice</th>
            <th id="Th8" runat="server">UnitsInStock</th>
            <th id="Th9" runat="server">UnitsOnOrder</th>
            <th id="Th10" runat="server">ReorderLevel</th>
          </tr>                                
          <tr ID="itemPlaceholder" runat="server">
          </tr>
        </table>
      </LayoutTemplate>
      <AlternatingItemTemplate>
        <tr class="alternate">
          <td rowspan="2">
            <asp:Label ID="ProductIDLabel" 
              runat="server" 
              Text='<%# Eval("ProductID") %>' />
          </td>
            <td colspan="7">
            <asp:Label ID="ProductNameLabel" 
              runat="server" 
              Text='<%# Eval("ProductName") %>' />
          </td>
          <td rowspan="2">
            <asp:CheckBox ID="DiscontinuedCheckBox" 
              runat="server" 
              Checked='<%# Eval("Discontinued") %>' 
              Enabled="false" />
          </td>
        </tr>
        <tr class="alternate">
          <td>
            <asp:Label ID="SupplierIDLabel" 
              runat="server" 
              Text='<%# Eval("SupplierID") %>' />
          </td>
          <td>
            <asp:Label ID="CategoryIDLabel" 
              runat="server" 
              Text='<%# Eval("CategoryID") %>' />
          </td>
          <td>
            <asp:Label ID="QuantityPerUnitLabel" 
              runat="server" 
              Text='<%# Eval("QuantityPerUnit") %>' />
          </td>
          <td>
            <asp:Label ID="UnitPriceLabel" 
              runat="server" 
              Text='<%# Eval("UnitPrice") %>' />
          </td>
          <td>
            <asp:Label ID="UnitsInStockLabel" 
              runat="server" 
              Text='<%# Eval("UnitsInStock") %>' />
          </td>
          <td>
            <asp:Label ID="UnitsOnOrderLabel" 
              runat="server" 
            Text='<%# Eval("UnitsOnOrder") %>' />
          </td>
            <td>
            <asp:Label ID="ReorderLevelLabel" 
               runat="server" 
               Text='<%# Eval("ReorderLevel") %>' />
          </td>
        </tr>
      </AlternatingItemTemplate>
      <ItemTemplate>
        <tr>
          <td rowspan="2">
            <asp:Label ID="ProductIDLabel" 
              runat="server" 
              Text='<%# Eval("ProductID") %>' />
          </td>
          <td colspan="7">
          <asp:Label ID="ProductNameLabel" 
            runat="server" 
            Text='<%# Eval("ProductName") %>' />
          </td>
            <td rowspan="2">
              <asp:CheckBox ID="DiscontinuedCheckBox" 
                runat="server" 
                Checked='<%# Eval("Discontinued") %>' 
                Enabled="false" />
            </td>
        </tr>
        <tr>
         <td>
           <asp:Label ID="Label1" 
             runat="server" 
             Text='<%# Eval("SupplierID") %>' />
         </td>
         <td>
           <asp:Label ID="Label2" 
             runat="server" 
             Text='<%# Eval("CategoryID") %>' />
         </td>
         <td>
           <asp:Label ID="Label3" 
             runat="server" 
             Text='<%# Eval("QuantityPerUnit") %>' />
         </td>
         <td>
           <asp:Label ID="Label4" 
             runat="server" 
             Text='<%# Eval("UnitPrice") %>' />
         </td>
         <td>
           <asp:Label ID="Label5" 
             runat="server" 
             Text='<%# Eval("UnitsInStock") %>' />
         </td>
         <td>
           <asp:Label ID="Label6" 
             runat="server" 
             Text='<%# Eval("UnitsOnOrder") %>' />
         </td>
         <td>
           <asp:Label ID="Label7" runat="server" 
              Text='<%# Eval("ReorderLevel") %>' />
         </td>
       <tr>
      </ItemTemplate>
    </asp:ListView>
    <asp:DataPager ID="DataPager1" 
      runat="server" 
      PageSize="7" 
      PagedControlID="ListView1">
      <Fields>
        <asp:NextPreviousPagerField 
          ButtonType="Link" 
          ShowFirstPageButton="True" 
          ShowNextPageButton="False" 
          ShowPreviousPageButton="False" 
          FirstPageText="&lt;&lt;最初" />
        <asp:NumericPagerField 
          ButtonCount="5"
          PreviousPageText="&lt;前の 5 ページ"
          NextPageText="次の 5 ページ&gt;"/>
        <asp:NextPreviousPagerField 
          ButtonType="Link" 
          ShowLastPageButton="True" 
          ShowNextPageButton="False" 
          ShowPreviousPageButton="False" 
          LastPageText="最後&gt;&gt;" />
      </Fields>
    </asp:DataPager>
  </div>
  </form>
</body>
</html>

先の記事の方法と今回の記事の方法とで、どちらが簡単かと言えば、正直言ってどっちもどっちって感じです。

Tags: , ,

Paging

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  March 2021  >>
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar