C# WinForms DataGridView 实现手动与自动翻页
本文详细讲解了在C# WinForms开发中,如何让表格控件DataGridView实现类似幻灯片或监控大屏的分页效果。主要包含两种方案:一是通过隐藏原生滚动条并结合坐标属性实现平滑的手动翻页;二是利用Timer定时器与数据集切片技术,实现数据的定时自动轮播。文中附带了完整的计算逻辑代码及注意事项。
手动翻页:消除滚动条并控制视口
默认情况下,DataGridView拥有内置的滚动条。要实现自定义分页(如翻页按钮),首先需要禁用系统的自动滚动行为,并将视图精准定位到第一行或最后一行。
第1步:禁用垂直滚动条
为了统一交互体验,避免用户误触滚轮导致页面错乱,应将滚动模式设置为无滚动条。
dataGridView1.ScrollBars = ScrollBars.None;
第2步:根据窗体自适应计算每页行数
若表格高度随窗口大小变化,需动态计算当前可视区域内能容纳的最大行数。此计算应在Resize事件中执行,以获取最新的空间尺寸:
int rowCount; // 每一页显示的行数
int maxCount; // 总页数
private void uc_table_Resize(object sender, EventArgs e)
{
this.Invalidate();
// 计算可用高度:表格总高度减去列头高度
int availableHeight = dataGridView1.Height - dataGridView1.ColumnHeadersHeight;
// 计算每页最大行数
if (dataGridView1.RowTemplate.Height > 0)
{
rowCount = availableHeight / dataGridView1.RowTemplate.Height;
}
else
{
// 兜底逻辑,防止除以零
rowCount = 10;
}
// 向上取整计算总页数
maxCount = (int)Math.Ceiling(dataGridView1.Rows.Count / (double)rowCount);
}
注意:如果表格初始化时已加载大量数据,RowTemplate的高度可能无法正确反映实际渲染高度。此时建议在设置 AutoGenerateColumns 之前或通过调整列头高度来确保 RowTemplate 准确对应实际行高。
第3步:指定起始显示行
核心属性为 FirstDisplayedScrollingRowIndex,它决定了屏幕最上方显示的第一行索引。
// 翻回第一页
dataGridView1.FirstDisplayedScrollingRowIndex = 0;
// 翻至最后一页
int lastRowIndex = Math.Max(0, dataGridView1.Rows.Count - rowCount);
dataGridView1.FirstDisplayedScrollingRowIndex = lastRowIndex;
上述方法通过简单的坐标偏移实现了流畅的切换效果。对于需要频繁跳转的数据操作,建议配合 Refresh() 调用以确保重绘及时。
进阶方案:定时器驱动的数据循环读取
在特定的展示场景(如数据看板、实时监控)下,可能需要数据自动按页轮换。下面提供一个基于 DataSet 和 Timer 的自动翻页示例。该示例假设每页固定显示5行,间隔2秒切换一次。
准备工作:初始化与数据库连接
首先配置窗体加载事件,获取数据总量并计算分页参数:
using System.Data;
using System.Data.SqlClient;
// ... 其他引用
public partial class Form1 : Form
{
private DataSet myDataSet = new DataSet();
private int DataRowsCountTotal = 0; // 总行数
private int PageCount = 0; // 满页页数
private int residualRowsCount = 0; // 剩余行数(最后一页不完整部分)
private int TakeCount = 0; // 当前页码(从0开始)
private void Form1_Load(object sender, EventArgs e)
{
// 建议使用配置文件管理连接字符串
string myConnectString = "Server=localhost;User Id=sa;Pwd=123;DataBase=test";
using (SqlConnection myConn = new SqlConnection(myConnectString))
{
myConn.Open();
SqlCommand myComm = new SqlCommand("SELECT * FROM Table_1 ORDER BY ID ASC", myConn);
SqlDataAdapter myAdap = new SqlDataAdapter(myComm);
myAdap.Fill(myDataSet, "Table_1");
}
// 基础数据计算
DataRowsCountTotal = myDataSet.Tables[0].Rows.Count;
int PageSize = 5; // 假设每页5行
PageCount = DataRowsCountTotal / PageSize;
residualRowsCount = DataRowsCountTotal % PageSize;
// UI 配置
dataGridView1.AutoGenerateColumns = false;
dataGridView1.RowHeadersVisible = false;
dataGridView1.AllowUserToAddRows = false;
// 启动定时器
timer1.Interval = 2000; // 2秒
timer1.Enabled = true;
// 绑定第一页
DataGrieDataBind(0);
}
}
第2步:数据切片与绑定
为了实现“虚拟”的分页而不重新请求数据库,可以将内存中的 DataSet 进行内存级切片。创建一个新的 DataTable 克隆结构并导入指定范围的数据:
private void DataGrieDataBind(int currentPageIndex)
{
DataTable myDt = new DataTable();
myDt = myDataSet.Tables[0].Clone(); // 克隆表结构
int pageSize = 5; // 每页行数需与计算时保持一致
int totalRows = DataRowsCountTotal;
bool isLastPage = (currentPageIndex + 1 > PageCount);
int startIndex = currentPageIndex * pageSize;
int endIndex;
if (isLastPage)
{
// 处理最后一页,可能不满5行
endIndex = totalRows - 1;
}
else
{
// 处理中间页,共5行
endIndex = startIndex + pageSize - 1;
}
for (int i = startIndex; i <= endIndex && i < totalRows; i++)
{
myDt.ImportRow(myDataSet.Tables[0].Rows[i]);
}
myDt.AcceptChanges();
this.dataGridView1.DataSource = myDt;
}
第3步:定时器触发翻页逻辑
在 Timer 的事件处理程序中更新计数器,并在到达末页后重置为首页:
private void timer1_Tick(object sender, EventArgs e)
{
TakeCount++;
// 判断是否超出总页数
if (TakeCount > PageCount)
{
TakeCount = 0; // 回到第一页
}
DataGrieDataBind(TakeCount);
}
这种实现方式的优势在于无需频繁读写数据库,所有延迟发生在客户端内存计算中。但需注意,当数据量极大时,整个数据集装入 DataSet 会造成内存溢出,此时应改回第一种手动定位方案或直接优化 SQL 查询。
风险提示与局限性
- 大数据量性能警告: 将数万甚至更多行数据一次性载入
DataTable/DataSet会消耗大量内存,并可能导致UI线程阻塞卡顿。对于生产环境的大数据表,强烈建议在 SQL 层面使用T-SQL或OFFSET/FETCH进行物理分页。 - 内存碎片: 频繁创建临时
DataTable对象并绑定 DataSource 可能会引起 GC 压力,高频应用场景可考虑复用 DataTable 实例并清空行后再填充。
总结
C# DataGridView 的分页可以通过控制显示首行索引来实现轻量级的客户端翻页,适用于中小数据量的展示需求;而结合定时器的全量数据切片则更适合大屏循环展示。在处理具体业务时,请根据数据规模选择内存计算还是数据库查询策略,以避免性能瓶颈。 以上就是 C# DataGridView 手动与自动分页控制的详细内容,更多关于 C# 表格控件优化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













