Triển khai phân trang cho DataGridView trong Windows Forms với WebService

Để hiển thị dữ liệu có phân trang trên DataGridView trong ứng dụng Windows Forms kết nối qua WebService, cần thiết lập cơ chế truyền – nhận dữ liệu dạng tuần tự hóa (serialization), kiểm soát luồng yêu cầu và xử lý logic phân trang cả ở phía client lẫn server.

1. Thành phần phía client

Client khởi tạo một đối tượng PagedResult<Product> để chứa kết quả phân trang (bao gồm danh sách bản ghi hiện tại, tổng số trang, chỉ số trang hiện hành…). Hàm truy vấn thực hiện các bước sau:

  • Gọi hàm xác thực SOAP header trước khi gửi yêu cầu đến WebService.
  • Lấy điều kiện tìm kiếm từ điều khiển nhập liệu (ví dụ: txtSearchTerm.Text).
  • Gửi yêu cầu tới phương thức FetchProductsByCriteria với các tham số: chuỗi tìm kiếm, trạng thái sản phẩm, ID người dùng, số trang hiện tại (currentPage) và kích thước trang (itemsPerPage).
  • Nhận chuỗi XML đã được mã hóa URL làm đầu ra, sau đó giải mã và khôi phục thành đối tượng PagedResult<Product> bằng tiện ích mở rộng XmlDeserializer.
  • Thiết lập AutoGenerateColumns = false để kiểm soát cột hiển thị, gán nguồn dữ liệu trực tiếp từ thuộc tính Data, đồng thời cập nhật nhãn hiển thị số trang.
private PagedResult<Product> currentResult;

private bool LoadProducts(string searchTerm, ProductStatus status, int currentPage)
{
    AuthenticateSoapHeader(); // Xác thực qua SOAP header

    string encodedQuery = Uri.EscapeDataString(searchTerm ?? "");
    string response = productService.FetchProductsByCriteria(
        encodedQuery,
        status,
        currentUser.Id,
        currentPage,
        ItemsPerPage
    );

    if (string.IsNullOrEmpty(response))
        return false;

    currentResult = XmlDeserializer.FromXml<PagedResult<Product>>(
        Uri.UnescapeDataString(response)
    );

    dataGridView1.AutoGenerateColumns = false;
    dataGridView1.DataSource = currentResult.Data;
    dataGridView1.ClearSelection();

    lblTotalPages.Text = currentResult.TotalPages.ToString();
    lblCurrentPage.Text = currentResult.CurrentPage.ToString();

    return true;
}

2. Thành phần phía WebService

Phương thức WebMethod được đánh dấu với [SoapHeader("authHeader")][WebMethod(EnableSession = true)]. Sau khi xác thực thành công, hệ thống xây dựng truy vấn LINQ dựa trên điều kiện tìm kiếm và trạng thái sản phẩm, sau đó áp dụng phân trang bằng lớp PagedResult<T>:

[SoapHeader("authHeader")]
[WebMethod(EnableSession = true)]
public string FetchProductsByCriteria(string encodedSearchTerm, ProductStatus status, string userId, int page, int size)
{
    if (!authHeader.IsValid())
        return Uri.EscapeDataString(new PagedResult<Product>(null, page, size).ToXml());

    string searchTerm = Uri.UnescapeDataString(encodedSearchTerm);
    using var repo = new DataRepository();

    IQueryable<Product> baseQuery = from p in repo.Products
                                       join d in repo.ProductDetails on p.Id equals d.ProductId
                                       where (p.OwnerId == userId || d.SellerId == userId) && p.Status == status
                                       select p;

    if (!string.IsNullOrWhiteSpace(searchTerm))
    {
        baseQuery = baseQuery.Where(p => 
            p.Name.Contains(searchTerm) || 
            p.Code.StartsWith(searchTerm) || 
            p.Description.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0
        );
    }

    var result = new PagedResult<Product>(baseQuery, page, size);
    return Uri.EscapeDataString(result.ToXml());
}

3. Lớp hỗ trợ phân trang PagedResult<T>

Lớp này đóng vai trò trung tâm trong việc đóng gói kết quả phân trang. Constructor thực hiện tính toán tổng số trang, đảm bảo chỉ số trang nằm trong giới hạn hợp lệ, và sử dụng Skip() + Take() để lấy tập con tương ứng:

public class PagedResult<T>
{
    public List<T> Data { get; set; }
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
    public int TotalCount { get; set; }
    public int PageSize { get; set; }

    public PagedResult(IQueryable<T> source, int pageIndex, int pageSize)
    {
        Data = new List<T>();
        PageSize = pageSize;
        CurrentPage = Math.Max(1, Math.Min(pageIndex, GetMaxPage(source, pageSize)));

        if (source?.Any() == true)
        {
            TotalCount = source.Count();
            TotalPages = (int)Math.Ceiling(TotalCount / (double)pageSize);

            Data = source
                .Skip((CurrentPage - 1) * pageSize)
                .Take(pageSize)
                .ToList();
        }
    }

    private static int GetMaxPage(IQueryable<T> source, int pageSize) =>
        source == null ? 1 : (int)Math.Ceiling(source.Count() / (double)pageSize);

    public string ToXml() => this.SerializeToXml();
}

Thẻ: WinForms DataGridView webservice xml-serialization paging

Đăng vào ngày 21 tháng 6 lúc 04:20