窗体应用分页查询
窗体应用(WinForm)分页查询
适用场景:WinForm 客户端数据库数据展示、DataGridView 表格分页查询、批量数据加载优化
核心目的:避免一次性加载海量数据导致窗体卡顿、内存溢出,实现按需分页加载数据,提升客户端流畅度
**
一、分页核心原理
**
窗体分页查询本质是 数据库端分页(核心)。客户端传递分页参数给数据库,数据库只返回当前页数据,最大限度减少数据传输和内存占用。
- 必备分页参数
- 页码(PageIndex):当前展示的页数,默认从 1 开始
- 页容量(PageSize):每页展示的数据条数,常用 10、20、50
- 总条数(TotalCount):符合查询条件的所有数据总数,用于计算总页数
- 总页数(TotalPage):根据总条数和页容量计算得出
- 核心计算公式
总页数 = 总条数 % 页容量 == 0 ? 总条数 / 页容量 : 总条数 / 页容量 + 1
数据偏移量(SQL 跳过条数)= (页码 - 1) * 页容量
**
二、SQL 分页查询语句(SQL Server)
**
1. 基础分页语句:
– 分页查询数据
SELECT * FROM 数据表名
WHERE 筛选条件
ORDER BY 主键/排序字段 – 分页必须排序,否则数据会错乱
OFFSET (@PageIndex-1)@PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;
– 查询符合条件的总数据条数(用于计算分页)
SELECT COUNT() FROM 数据表名 WHERE 筛选条件;
- 语句说明
- OFFSET:跳过指定条数的数据
- FETCH NEXT … ROWS ONLY:读取指定条数的数据
- 必须加 ORDER BY:数据库默认无序,不加排序会导致每页数据重复、错乱
三、通用分页实体类
// 当前页数据集合
public List DataList { get; set; } = new List();
// 符合条件的数据总条数
public int TotalCount { get; set; }
// 计算后的总页数
public int TotalPage { get; set; }
///
/// 当前页码
///
public int PageIndex { get; set; }
// 每页展示条数
public int PageSize { get; set; }
// 统一计算总页数
public void ComputeTotalPage()
{
if (PageSize <= 0)
{
TotalPage = 0;
return;
}
TotalPage = (int)Math.Ceiling(TotalCount / (double)PageSize);
}
public class PageQueryParam
{
// 默认第一页
public int PageIndex { get; set; } = 1;
// 项目统一默认每页10条
public int PageSize { get; set; } = 10;
// 计算跳过数据数
public int GetSkip()
{
return (PageIndex - 1) * PageSize;
}
}
四、核心分页查询业务
封装通用分页查询方法,传入 SQL、分页参数,直接返回分页结果,适配所有数据表。
/// 对应数据库实体
/// 条件语句,不带ORDER BY、不带分页
/// 排序语句(必填,例如:ORDER BY Id DESC)
/// 分页参数
/// SQL防注入参数
/// 分页结果实体
public PageResult GetPageList(string whereSql, string orderBySql, PageQueryParam pageParam, params SqlParameter[] sqlParams) where T : new()
{
PageResult pageResult = new PageResult();
pageResult.PageIndex = pageParam.PageIndex;
pageResult.PageSize = pageParam.PageSize;
// 1. 拼接统计总条数SQL
string countSql = $“SELECT COUNT(*) FROM 表名 WHERE 1=1 {whereSql}”;
int totalCount = Convert.ToInt32(SqlHelper.ExecuteScalar(countSql, sqlParams));
pageResult.TotalCount = totalCount;
pageResult.ComputeTotalPage();
// 无数据直接返回空
if (totalCount == 0)
return pageResult;
// 2. 拼接分页查询SQL(项目固定OFFSET FETCH写法)
string dataSql = $@“SELECT * FROM T_UserInfo
WHERE 1=1 {whereSql}
{orderBySql}
OFFSET @Skip ROWS
FETCH NEXT @PageSize ROWS ONLY”;
// 3. 新增分页参数
var paramList = sqlParams.ToList();
paramList.Add(new SqlParameter(“@Skip”, pageParam.GetSkip()));
paramList.Add(new SqlParameter(“@PageSize”, pageParam.PageSize));
// 4. 查询并绑定数据
pageResult.DataList = SqlHelper.QueryBySql(dataSql, paramList.ToArray());
return pageResult;
五、窗体前端分页控件交互逻辑
窗体界面常用:上一页、下一页、首页、末页、页码输入、每页条数选择 功能
1. 窗体全局分页变量
// 分页入参实例
private PageQueryParam _pageParam = new PageQueryParam();
// 分页结果实例
private PageResult _pageResult = new PageResult();
// 页面搜索条件
private string _searchKey = string.Empty;
2. 核心加载数据方法
///
/// 本周项目通用加载分页数据方法
/// 包含模糊查询、条件筛选、分页绑定、界面刷新全套逻辑
/// 所有分页页面直接调用该方法即可
///
private void LoadDataByPage()
{
try
{
// 1. 初始化条件拼接
string where = string.Empty;
List paramList = new List();
// 模糊查询条件
if (!string.IsNullOrEmpty(_searchKey))
{
where += " AND UserName LIKE @SearchKey OR Phone LIKE @SearchKey";
paramList.Add(new SqlParameter(“@SearchKey”, $“%{_searchKey}%”));
}
// 固定筛选:只查询未删除数据
where += " AND IsDelete = 0";
// 2. 调用通用分页方法
_pageResult = GetPageList(where, “ORDER BY CreateTime DESC, Id DESC”, _pageParam, paramList.ToArray());
// 3. 绑定DataGridView
dgv_Data.AutoGenerateColumns = false;
dgv_Data.DataSource = null;
dgv_Data.DataSource = _pageResult.DataList;
// 4. 刷新分页状态栏文本
lblPageTip.Text =KaTeX parse error: Expected 'EOF', got '}' at position 91: …TotalPage} 页"; }̲ } catch (Excep…“数据加载失败:{ex.Message}”, “系统提示”, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
3. 分页按钮点击事件
// 首页
private void btnFirst_Click(object sender, EventArgs e)
{
_pageParam.PageIndex = 1;
LoadDataByPage();
}
// 上一页
private void btnPrev_Click(object sender, EventArgs e)
{
if (_pageParam.PageIndex > 1)
{
_pageParam.PageIndex–;
LoadDataByPage();
}
}
// 下一页
private void btnNext_Click(object sender, EventArgs e)
{
if (_pageParam.PageIndex < _pageResult.TotalPage)
{
_pageParam.PageIndex++;
LoadDataByPage();
}
}
// 末页
private void btnLast_Click(object sender, EventArgs e)
{
_pageParam.PageIndex = _pageResult.TotalPage;
LoadDataByPage();
}
// 搜索按钮点击事件
private void btnSearch_Click(object sender, EventArgs e)
{
// 搜索重置为第一页
_pageParam.PageIndex = 1;
_searchKey = txtSearch.Text.Trim();
LoadDataByPage();
}
// 重置按钮
private void btnReset_Click(object sender, EventArgs e)
{
txtSearch.Clear();
_searchKey = string.Empty;
_pageParam.PageIndex = 1;
LoadDataByPage();
}
六、常见问题与踩坑记录
- 分页数据重复/错乱
原因:未添加 ORDER BY 排序,数据库分页依赖有序数据集。
解决:分页 SQL 必须添加稳定排序(主键、时间戳)。 - 切换页码数据不刷新
原因:DataGridView 数据源未清空,缓存残留旧数据。
解决:绑定新数据前先设置DataSource = null。 - 总页数计算错误
原因:未对取余结果判断,直接整除。
解决:使用笔记中封装的CalcTotalPage 方法自动计算。 - 页码越界报错
解决:切换页码前判断,当前页码不能小于1、不能大于总页数。