要解決的目標:如何讓 Datagridview 快速平滑顯示大量數據
通常,Winform 下的表格控件是很“低效”的,如 DataGrid 和 DataGridView。造成低效的原因在於在默認的設定下,它們都誠實的和數據源做了“真綁定”,這種綁定無論你使用了那種方式對數據源進行載入和管理,表格控件都會和全部的數據一一進行認識,並根據它們的數量和類型,逐個創建行和單元格。——也就是說,數據源有1萬個單元格,表格控件默認就會對這1萬個數據進行認識和創建並顯示出來,怎么能不慢。
注:數據源是指和表格控件綁定的或通過其它方式對表格控件賦值的所有數據來源,並非數據庫中的所有數據。
解決方式兩點,虛模式顯示和雙緩沖加持。
虛模式,VirtualMode = true。是 DataGridView 中一個屬性,當開啟時顯示轉為虛模式顯示,和不開啟的區別就是
不開啟時,顯示不顯示,用不用的着的單元格都會被創建和使用,開啟后,只有當單元格顯示或被使用時,才按需引發事件
個人理解就是,虛模式當某位置單元格需要被顯示或處理業務時,表格控件會通過引發各類事件通知代碼,要求提供下一步動作,從而大幅度節省“不需要的開銷”。比如一個窗體上能顯示100個單元格時,就對這100個做請求,編碼人員可以根據位置提供這100個單元格的內容,窗體上永遠最多顯示100個。位置是固定的,內容是變化的。
實現方法是:
- 首先開啟 VirtualMode = true
- 為 DataGridView 添加列,用於顯示數據
- 為 DataGridView 添加行,這將會引發RowNeeded事件和CellValueNeeded事件,從而可以在事件中處理對被需要的單元格賦予何值。
const int initialSize = 3000;
//get data
dataPool = await DataTransfer.GetData(AppConfig.DbConnectionString, "grades", 1, 3000);
dgvShow.Rows.Clear();
dgvShow.Columns.Clear();
dgvShow.VirtualMode = true;
dgvShow.ReadOnly = true;
foreach(DataColumn col in dataPool.Columns)
{
dgvShow.Columns.Add(col.ColumnName, col.ColumnName);
}
//此句用於控制 DataGridView 顯示行數,同時起到添加行引發事件的作用
dgvShow.RowCount = initialSize;
private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
if (dataPool != null)
{
e.Value = dataPool.Rows[e.RowIndex][e.ColumnIndex];
}
}
上面的代碼,先獲取到擁有3000行數據的 DataTable,然后設定 DataGridView 開啟虛模式並“創建”行來引發相關事件。在 CellValueNeeded 中將數據表中對應數據顯示到表格控件中。
上面的行為只是解決了部分問題,避免了表格控件的性能的嚴重浪費,但數據較多時會發現拉動時依舊會“卡”,這就要解決平滑顯示的問題。
使用雙緩沖來解決
using System.Reflection;
...
public void DoubleBuffered(DataGridView dgv, bool setting)
{
Type dgvType = dgv.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dgv, setting, null);
}
上面的代碼利用反射,對表格控件的 DoubleBuffered 屬性進行設定,為什么不能直接設定?因為其 DoubleBuffered 是 protected 的,不使用反射無法訪問。