Phân trang cho DataGridView trong C#

Tại sao cần phân trang?

  • Với các ứng dụng trên web việc phân trang rất quan trọng để tăng hiệu năng khi load trang. Trên WinForm, thường các ứng dụng chạy trên local hoặc trong mạng cục bộ nên nhiều khi ta không quan tâm đến việc phân trang. Nhưng với những bảng dữ liệu hàng ngàn bản ghi, sẽ không hiệu quả trong việc hiển thị, việc nạp dữ liệu hàng ngàn bản ghi lên lưới (Grid) sẽ làm hiệu năng của ứng dụng giảm và tiêu tốn bộ nhớ.
  • Phân trang thực hiện chia dữ liệu thành các phần nhỏ trên mỗi trang với số lượng bản ghi xác định. Khi cần sẽ nạp số lượng bản ghi của từng trang tương ứng. Số trang được tính toán trên tổng số lượng bản ghi.

Cơ sở dữ liệu

Tạo cơ sở dữ liệu QLBanHang trong SQL Server, trong đó có bảng:
tblMatHang(MaSP nchar(5), TenSP nvarchar(30), NgaySX Date, NgayHH Date, DonVi nvarchar(10), DonGia Float , GhiChu nvarchar(200))


Cách thức thực hiện

  • Đếm số lượng bản ghi RecordCount trong bảng dữ liệu, dùng câu lệnh SELECT COUNT(*)
  • Giả sử số lượng bản ghi hiển thị trên trang là PageSize, giả sử PageSize = 5
  • Giả sử trang hiện tại đang hiển thị là PageIndex, ban đầu PageIndex = 1.
  • Lấy những bản ghi nằm trong khoảng (PageIndex-1)*PageSize+1 tới PageIndex*PageSize. Từ phiên bản SQL Server 2005 cung cấp hàm ROW_NUMBER() để cho phép chúng ta thêm vào số dòng tới các bản ghi được chọn từ bảng dữ liệu. Để tối ưu chúng ta có thể tạo Stored Procedure trong SQL Server với việc lưu trữ PageIndex, PageSize như các biến đầu vào và RecordCount là biến đầu ra.
Tạo Stored Procedure
  • Mở Server Explorer> Chọn Database của bạn > Kích chuột vào Stored Procedure > Add New Stored Procedure
  • Copy câu lệnh SQL ở bên dưới rồi lưu lại
Tạo Store Procedured trong Visual Studio

Tạo Stored Procedure trong Visual Studio


CREATE PROCEDURE [dbo].[GetMatHangPaging]
      @PageIndex INT = 1
      ,@PageSize INT = 5
      ,@RecordCount INT OUTPUT
AS
BEGIN
      SET NOCOUNT ON;
      -- Lấy lữ liệu từ bảng dữ liệu MatHang và lưu vào bảng tạm tblTemp cùng với số dòng
      SELECT ROW_NUMBER() OVER
      (
            ORDER BY MaSP ASC
      )AS Hang
            , *
      INTO #tblTemp
      FROM tblMatHang
      --Đếm số lượng bản ghi
      SELECT @RecordCount = COUNT(*)
      FROM #tblTemp
         
      SELECT *
      FROM #tblTemp
      WHERE Hang BETWEEN(@PageIndex -1) * @PageSize + 1 AND(((@PageIndex -1) * @PageSize + 1) + @PageSize) - 1
      --Xóa bảng tạm tblTemp
      DROP TABLE #tblTemp
END


Thiết kế giao diện

Thêm form vào Project, rồi kéo các control trên thanh Toolbox vào Form

  • Control Panel: Name = pnlDieuHuong, Dock = Bottom
  • Control DataGridView: Name = dgvKetQua, Dock = Fill
Giao diện phân trang với DataGridView

Giao diện phân trang với DataGridView


Code chương trình

1. Imports các Namespaces

using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Collections.Generic;
2. Phân trang cho DataGridView
  • Trong sự kiện Form Load, thủ tục LoadData gọi thực thi Stored Procedure được viết ở trên và lấy các bản ghi từ bảng tblMatHang sử dụng đối tượng DataReader (SqlDataReader).
  • Đối tượng SqlDataReader đượng nạp vào DataTable và cuối cùng gắn dữ liệu trong DataTable vào DataGridView.
  • Khởi tạo giá trị PageSize = 5 và PageIndex = 1. Và lấy giá trị đầu ra RecordCountPageIndex đặt vào phương thức HienThiThanhDieuHuong được viết ở phía dưới.

 //Thiết lập số bản ghi trên 1 trang.
        int PageSize = 5;

        public frmPhanTrang()
        {
            InitializeComponent();
        }

        private void frmPhanTrang_Load(object sender, EventArgs e)
        {
            //Ban đầu nạp trang đầu tiên
            this.LoadData(1);
        }
        private void LoadData(int pageIndex)
        {
            //Chuỗi kết nối
            string constring = @"Data Source=.\SQLEXPRESS;AttachDbFilename=" + Application.StartupPath + @"\QLBanHang.mdf;Integrated Security=True;User Instance=True";
            using (SqlConnection con = new SqlConnection(constring))
            {
                //Gọi câu lệnh SQL lưu trữ trong Stored Procedure có tên GetMatHangPaging
                using (SqlCommand cmd = new SqlCommand("GetMatHangPaging", con))
                {
                    //Truyền vào các tham số PageIndex, PageSize cho Stored Procedure
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@PageIndex", pageIndex);
                    cmd.Parameters.AddWithValue("@PageSize", PageSize);
                    //Lấy ra tham số RecordCount của Store Procedured
                    cmd.Parameters.Add("@RecordCount", SqlDbType.Int, 4);
                    cmd.Parameters["@RecordCount"].Direction = ParameterDirection.Output;
                    con.Open();
                    //Thực thi câu lệnh truy vấn và gán cho đối tượng DataTable
                    DataTable dt = new DataTable();
                    dt.Load(cmd.ExecuteReader());
                    dgvKetQua.DataSource = dt;
                    con.Close();
                    int recordCount = Convert.ToInt32(cmd.Parameters["@RecordCount"].Value);
                    this.HienThiThanhDieuHuong(recordCount, pageIndex);
                }
            }
        }
3. Hiển thị thanh điều hướng các trang
  • Phương thức HienThiThanhDieuHuong hiển thị các bản ghi và trang PageIndex hiện tại. Các tính toán được thực hiện để đặt trang đầu tiên, trang cuối và danh sách các trang được phát sinh. Sử dụng một class Page để lưu trữ danh sách các button.
  • Cuối cùng dùng vòng lặp để hiển thị các trang và các button phân trang được thêm vào control pnlDieuHuong.
  • Mỗi Button động sẽ khởi gán sự kiện click. Khi Button được kích, giá trị của Name được đặt như một tham số PageIndex tới hàm LoadData và sẽ gắn lại dữ liệu lên dgvKetQua.

private void HienThiThanhDieuHuong(int recordCount, int currentPage)
        {
            //Sử dụng đối tượng List để chứa danh sách các trang
            List pages = new List();
            int startIndex, endIndex;
            int pagerSpan = 5;

            //Tính toán để hiển thị các trang.
            double dblPageCount = (double)((decimal)recordCount / Convert.ToDecimal(PageSize));
            int pageCount = (int)Math.Ceiling(dblPageCount);
            startIndex = currentPage > 1 && currentPage + pagerSpan - 1 < pagerSpan ? currentPage : 1; endIndex = pageCount > pagerSpan ? pagerSpan : pageCount;
            if (currentPage > pagerSpan % 2)
            {
                if (currentPage == 2)
                {
                    endIndex = 5;
                }
                else
                {
                    endIndex = currentPage + 2;
                }
            }
            else
            {
                endIndex = (pagerSpan - currentPage) + 1;
            }

            if (endIndex - (pagerSpan - 1) > startIndex)
            {
                startIndex = endIndex - (pagerSpan - 1);
            }

            if (endIndex > pageCount)
            {
                endIndex = pageCount;
                startIndex = ((endIndex - pagerSpan) + 1) > 0 ? (endIndex - pagerSpan) + 1 : 1;
            }

            //Add nút trang đầu.
            if (currentPage > 1)
            {
                pages.Add(new Page { Text = "Trang đầu", Value = "1" });
            }

            //Add nút < 1)
            {
                pages.Add(new Page { Text = "<<", Value = (currentPage - 1).ToString() });
            }

            for (int i = startIndex; i <= endIndex; i++) { pages.Add(new Page { Text = i.ToString(), Value = i.ToString(), Selected = i == currentPage }); } //Add nút >>.
            if (currentPage < pageCount) { pages.Add(new Page { Text = ">>", Value = (currentPage + 1).ToString() });
            }

            //Add nút Trang cuối.
            if (currentPage != pageCount)
            {
                pages.Add(new Page { Text = "Trang cuối", Value = pageCount.ToString() });
            }

            //Xóa các nút trên trang.
            pnlDieuHuong.Controls.Clear();

            //Lặp và add các Buttons cho trang.
            int count = 0;
            foreach (Page page in pages)
            {
                Button btnPage = new Button();
                btnPage.Location = new System.Drawing.Point(100 * count, 20);
                btnPage.Size = new System.Drawing.Size(100, 50);
                btnPage.Name = page.Value;
                btnPage.Text = page.Text;
                btnPage.Enabled = !page.Selected;
                btnPage.Click += new System.EventHandler(this.Page_Click);
                pnlDieuHuong.Controls.Add(btnPage);
                count++;
            }
        }
        //Viết sự kiện khi kích trên nút phân trang
        private void Page_Click(object sender, EventArgs e)
        {
            Button btnPager = (sender as Button);
            this.LoadData(int.Parse(btnPager.Name));
        }
        //Tạo class Page để lưu trữ các thuộc tính của Page
        public class Page
        {
            public string Text { get; set; }
            public string Value { get; set; }
            public bool Selected { get; set; }
        }

Có thể bạn sẽ thích…

3 phản hồi

  1. Trần Hoàng Giang viết:

    Đoạn List pages = new List(); em dùng Visual Studio 2010 bị lỗi ạ. Nên sửa thành List pages = new List();

  2. nhocsa2k2 viết:

    đâu phải vậy đâu, Đoạn List pages = new List(); em đổi sang thành List pages = new List(); nó mới chạy

Trả lời Trần Hoàng Giang Hủy

EnglishVietnamese