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
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
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 RecordCount và PageIndex đặ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; }
}
Đ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();
List *dấu nhỏ hơn*page*dấu lớn hơn* = new List*dấu nhỏ hơn* page *dấu lớn hơn*();
đâ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