实现虚拟模式的动态数据加载Windows窗体DataGridView控件 .net 4.5 (一)
实现虚拟模式的即时数据加载Windows窗体DataGridView控件
如果你正在与一个非常大的表在一个远程数据库,例如,您可能希望避免启动延迟,只检索所需的数据显示和检索额外的数据只有当用户新行滚动到视图。 如果客户端电脑运行您的应用程序有一个有限的可用内存来存储数据,您可能还想丢弃未使用的数据从数据库中检索新值。
下面的部分描述了如何使用 DataGridView控制与实时缓存。
复制这个主题作为一个清单中的代码,看看 如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制。
表单
以下代码示例定义了一个包含一个只读形式 DataGridView与交互的控制 缓存 对象通过一个 CellValueNeeded事件处理程序。 的 缓存 对象管理和使用本地存储的值 DataRetriever 从订单表对象检索值的样本Northwind数据库。 的 DataRetriever 对象,它实现了 IDataPageRetriever 所需的接口 缓存 类,还用于初始化 DataGridView控制行和列。
的 IDataPageRetriever , DataRetriever , 缓存 类型被描述在这个话题。
请注意 |
---|
存储敏感信息,如密码,连接字符串内会影响应用程序的安全。 使用Windows身份验证(也称为集成安全)是一种更安全的方法来控制访问数据库。 有关更多信息,请参见 保护连接信息。 |
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms; public class VirtualJustInTimeDemo : System.Windows.Forms.Form
{
private DataGridView dataGridView1 = new DataGridView();
private Cache memoryCache; // Specify a connection string. Replace the given value with a
// valid connection string for a Northwind SQL Server sample
// database accessible to your system.
private string connectionString =
"Initial Catalog=NorthWind;Data Source=localhost;" +
"Integrated Security=SSPI;Persist Security Info=False";
private string table = "Orders"; protected override void OnLoad(EventArgs e)
{
// Initialize the form.
this.AutoSize = true;
this.Controls.Add(this.dataGridView1);
this.Text = "DataGridView virtual-mode just-in-time demo"; // Complete the initialization of the DataGridView.
this.dataGridView1.Size = new Size(, );
this.dataGridView1.Dock = DockStyle.Fill;
this.dataGridView1.VirtualMode = true;
this.dataGridView1.ReadOnly = true;
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.AllowUserToOrderColumns = false;
this.dataGridView1.SelectionMode =
DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.CellValueNeeded += new
DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded); // Create a DataRetriever and use it to create a Cache object
// and to initialize the DataGridView columns and rows.
try
{
DataRetriever retriever =
new DataRetriever(connectionString, table);
memoryCache = new Cache(retriever, );
foreach (DataColumn column in retriever.Columns)
{
dataGridView1.Columns.Add(
column.ColumnName, column.ColumnName);
}
this.dataGridView1.RowCount = retriever.RowCount;
}
catch (SqlException)
{
MessageBox.Show("Connection could not be established. " +
"Verify that the connection string is valid.");
Application.Exit();
} // Adjust the column widths based on the displayed values.
this.dataGridView1.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.DisplayedCells); base.OnLoad(e);
} private void dataGridView1_CellValueNeeded(object sender,
DataGridViewCellValueEventArgs e)
{
e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex);
} [STAThreadAttribute()]
public static void Main()
{
Application.Run(new VirtualJustInTimeDemo());
} }
c# Code
IDataPageRetriever接口
下面的代码示例定义了 IDataPageRetriever 接口,它的实现 DataRetriever 类。 这个接口中声明是唯一的方法 SupplyPageOfData 方法,它需要一个初始的行数的行索引和计数在一个页面的数据。 这些值使用的实现者从数据源检索数据的一个子集。
一个 缓存 对象使用该接口的一个实现施工加载两个初始页面的数据。 每当一个未值是必要的,缓存丢弃其中一个页面,请求一个新的页面包含的值 IDataPageRetriever 。
public interface IDataPageRetriever
{
DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
}
DataRetriever类
下面的代码示例定义了 DataRetriever 类,它实现了 IDataPageRetriever 从服务器的数据接口来检索页面。 的 DataRetriever 类还提供了 列 和 rowcount 属性, DataGridView控制用于创建必要的列并添加适当的空行数 行收集。 添加空行是必要的,以便控制行为好像包含所有表中的数据。 这意味着在滚动条滚动框会有适当的大小,和用户将能够访问任何表中的行。 行是满的 CellValueNeeded事件处理程序只有当他们滚动到视图。
public class DataRetriever : IDataPageRetriever
{
private string tableName;
private SqlCommand command; public DataRetriever(string connectionString, string tableName)
{
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
command = connection.CreateCommand();
this.tableName = tableName;
} private int rowCountValue = -; public int RowCount
{
get
{
// Return the existing value if it has already been determined.
if (rowCountValue != -)
{
return rowCountValue;
} // Retrieve the row count from the database.
command.CommandText = "SELECT COUNT(*) FROM " + tableName;
rowCountValue = (int)command.ExecuteScalar();
return rowCountValue;
}
} private DataColumnCollection columnsValue; public DataColumnCollection Columns
{
get
{
// Return the existing value if it has already been determined.
if (columnsValue != null)
{
return columnsValue;
} // Retrieve the column information from the database.
command.CommandText = "SELECT * FROM " + tableName;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.FillSchema(table, SchemaType.Source);
columnsValue = table.Columns;
return columnsValue;
}
} private string commaSeparatedListOfColumnNamesValue = null; private string CommaSeparatedListOfColumnNames
{
get
{
// Return the existing value if it has already been determined.
if (commaSeparatedListOfColumnNamesValue != null)
{
return commaSeparatedListOfColumnNamesValue;
} // Store a list of column names for use in the
// SupplyPageOfData method.
System.Text.StringBuilder commaSeparatedColumnNames =
new System.Text.StringBuilder();
bool firstColumn = true;
foreach (DataColumn column in Columns)
{
if (!firstColumn)
{
commaSeparatedColumnNames.Append(", ");
}
commaSeparatedColumnNames.Append(column.ColumnName);
firstColumn = false;
} commaSeparatedListOfColumnNamesValue =
commaSeparatedColumnNames.ToString();
return commaSeparatedListOfColumnNamesValue;
}
} // Declare variables to be reused by the SupplyPageOfData method.
private string columnToSortBy;
private SqlDataAdapter adapter = new SqlDataAdapter(); public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
{
// Store the name of the ID column. This column must contain unique
// values so the SQL below will work properly.
if (columnToSortBy == null)
{
columnToSortBy = this.Columns[].ColumnName;
} if (!this.Columns[columnToSortBy].Unique)
{
throw new InvalidOperationException(String.Format(
"Column {0} must contain unique values.", columnToSortBy));
} // Retrieve the specified number of rows from the database, starting
// with the row specified by the lowerPageBoundary parameter.
command.CommandText = "Select Top " + rowsPerPage + " " +
CommaSeparatedListOfColumnNames + " From " + tableName +
" WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
lowerPageBoundary + " " + columnToSortBy + " From " +
tableName + " Order By " + columnToSortBy +
") Order By " + columnToSortBy;
adapter.SelectCommand = command; DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.Fill(table);
return table;
} }
缓存类
下面的代码示例定义了 缓存 类,它管理通过一个两页的数据填充 IDataPageRetriever 实现。 的 缓存 类定义了一个内部 DataPage 结构,它包含一个 datatable将值存储在一个缓存页面和计算的行索引代表页面的上下边界。
的 缓存 在施工时类加载两个页面的数据。 每当 CellValueNeeded事件请求值, 缓存 对象确定是否可用的值是在一个两页的,如果是,返回它。 如果该值不是本地可用的 缓存 对象决定了它的两页是最远的来自当前显示的行和替换包含请求的页面,一个新的值,然后返回。
假设在数据页的行数的行数相同,可以显示在屏幕上,该模型允许用户通过表分页高效地返回到最近查看的页面。
public class Cache
{
private static int RowsPerPage; // Represents one page of data.
public struct DataPage
{
public DataTable table;
private int lowestIndexValue;
private int highestIndexValue; public DataPage(DataTable table, int rowIndex)
{
this.table = table;
lowestIndexValue = MapToLowerBoundary(rowIndex);
highestIndexValue = MapToUpperBoundary(rowIndex);
System.Diagnostics.Debug.Assert(lowestIndexValue >= );
System.Diagnostics.Debug.Assert(highestIndexValue >= );
} public int LowestIndex
{
get
{
return lowestIndexValue;
}
} public int HighestIndex
{
get
{
return highestIndexValue;
}
} public static int MapToLowerBoundary(int rowIndex)
{
// Return the lowest index of a page containing the given index.
return (rowIndex / RowsPerPage) * RowsPerPage;
} private static int MapToUpperBoundary(int rowIndex)
{
// Return the highest index of a page containing the given index.
return MapToLowerBoundary(rowIndex) + RowsPerPage - ;
}
} private DataPage[] cachePages;
private IDataPageRetriever dataSupply; public Cache(IDataPageRetriever dataSupplier, int rowsPerPage)
{
dataSupply = dataSupplier;
Cache.RowsPerPage = rowsPerPage;
LoadFirstTwoPages();
} // Sets the value of the element parameter if the value is in the cache.
private bool IfPageCached_ThenSetElement(int rowIndex,
int columnIndex, ref string element)
{
if (IsRowCachedInPage(, rowIndex))
{
element = cachePages[].table
.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
return true;
}
else if (IsRowCachedInPage(, rowIndex))
{
element = cachePages[].table
.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
return true;
} return false;
} public string RetrieveElement(int rowIndex, int columnIndex)
{
string element = null; if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
{
return element;
}
else
{
return RetrieveData_CacheIt_ThenReturnElement(
rowIndex, columnIndex);
}
} private void LoadFirstTwoPages()
{
cachePages = new DataPage[]{
new DataPage(dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(), RowsPerPage), ),
new DataPage(dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(RowsPerPage),
RowsPerPage), RowsPerPage)};
} private string RetrieveData_CacheIt_ThenReturnElement(
int rowIndex, int columnIndex)
{
// Retrieve a page worth of data containing the requested value.
DataTable table = dataSupply.SupplyPageOfData(
DataPage.MapToLowerBoundary(rowIndex), RowsPerPage); // Replace the cached page furthest from the requested cell
// with a new page containing the newly retrieved data.
cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex); return RetrieveElement(rowIndex, columnIndex);
} // Returns the index of the cached page most distant from the given index
// and therefore least likely to be reused.
private int GetIndexToUnusedPage(int rowIndex)
{
if (rowIndex > cachePages[].HighestIndex &&
rowIndex > cachePages[].HighestIndex)
{
int offsetFromPage0 = rowIndex - cachePages[].HighestIndex;
int offsetFromPage1 = rowIndex - cachePages[].HighestIndex;
if (offsetFromPage0 < offsetFromPage1)
{
return ;
}
return ;
}
else
{
int offsetFromPage0 = cachePages[].LowestIndex - rowIndex;
int offsetFromPage1 = cachePages[].LowestIndex - rowIndex;
if (offsetFromPage0 < offsetFromPage1)
{
return ;
}
return ;
} } // Returns a value indicating whether the given row index is contained
// in the given DataPage.
private bool IsRowCachedInPage(int pageNumber, int rowIndex)
{
return rowIndex <= cachePages[pageNumber].HighestIndex &&
rowIndex >= cachePages[pageNumber].LowestIndex;
} }
额外的注意事项
前面的代码示例提供示范的即时数据加载。 你需要为自己的需要修改代码实现最大效率。 至少,你需要选择一个适当的值为每页的行数的数据在缓存中。 这个值被传递到 缓存 构造函数。 每页的行数应不少于同时可以显示在你的行数 DataGridView控制。
为达到最佳效果,你需要进行性能测试和可用性测试,以确定您的系统和用户的要求。 几个因素需要考虑包括在客户端机器的内存数量运行您的应用程序,使用的网络连接的可用带宽,服务器的延迟。 带宽和延迟高峰期应该决定使用。
提高滚动您的应用程序的性能,您可以增加本地存储的数据量。 改进启动时间,但是,你必须避免最初加载太多数据。 您可能想要修改 缓存 类增加数据页的数量可以存储。 使用更多的数据页可以提高滚动效率,但您将需要确定理想的数据页的行数,根据可用带宽和服务器延迟。 较小的页面,服务器将被更频繁地访问,但会花更少的时间返回所请求的数据。 如果延迟比带宽的问题,您可能需要使用更大的数据页。
实现虚拟模式的动态数据加载Windows窗体DataGridView控件 .net 4.5 (一)的更多相关文章
- C#:将空间数据加载到树视图控件
自己 整理了 下 代码 测试了下 还行... #region 操作树视图控件 /// <summary> /// 自定义需要的类型 /// </summary> enum Da ...
- 【微信小程序】模仿58同城页面制作以及动态数据加载
完成动态数据的加载,如下 使用上班的空余时间慢慢的学习,相信总有一天我会很熟悉的掌握这门技术. 本次学习小总结: 微信小程序使用的代码基本与HTML.CSS.JS等前段有关知识一样. 微信小程序js使 ...
- 在ASP.NET中动态加载内容(用户控件和模板)
在ASP.NET中动态加载内容(用户控件和模板) 要点: 1. 使用Page.ParseControl 2. 使用base.LoadControl 第一部分:加载模板 下 面是一个模板“<tab ...
- WPF中动态加载XAML中的控件
原文:WPF中动态加载XAML中的控件 using System; using System.Collections.Generic; using System.Linq; using System. ...
- MVC4加载zTree树小控件
前言: 第一次学习使用MVC框架,找了个练手项目,加载zTree树小控件.下面我就一步步说明我这次练手的经历以记录.如果有什么错误,希望各位大神帮忙指正,谢谢. 第一步: 利用VS2010新建一个MV ...
- 上拉加载下拉刷新控件WaterRefreshLoadMoreView
上拉加载下拉刷新控件WaterRefreshLoadMoreView 效果: 源码: // // SRSlimeView // @author SR // Modified by JunHan on ...
- Echarts通过Ajax实现动态数据加载
Echarts(3.x版)官网实例的数据都是静态的,实际使用中往往会要求从服务器端取数据进行动态显示,官网教程里给出的异步数据加载很粗略,下面就以官网最简单的实例为例子,详细演示如下过程:1.客户端通 ...
- ECharts动态数据加载
最近有用到ECharts做可视化报表,小结一下 一.准备数据 1.官网下载echarts.min.js 2.引入jquery.js 3.请求用的json数据 { "list":[ ...
- ios ableviewcell的动态加载数据,模仿喜马拉雅动态数据加载
iphone(UITableViewCell)动态加载图片http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Intr ...
随机推荐
- Halcon相机标定
摄像机标定程序: 注意:E:/calibration_image :为标定图像文件路径 'E:/calibration_description/caltab_123mm.descr:为标定 ...
- Git忽略配置文件gitignore
在git客户端用户工程根目录下,.git同级目录创建文件.gitignore,文件名为.gitignore.目的是为了忽略一些不需要提交的文件到git服务器 配置如下: bin target .set ...
- ecshop登陆后价格可见,会员注册登陆才能显示价格
打开模版文件夹里面的goods.dwt 查找{$lang.shop_price}<font class="price" id="ECS_SHOPPRICE" ...
- EntityFramework Code First 添加唯一键
在EntityFramework 6.1后可以直接使用 [Index("TitleIndex", IsUnique = true)] public string Title { g ...
- mysql用shell建100多字段表并导入
excel列超过160多个,导入时报错,把excel第一行另存为逗号分隔的csv文件,用shell建表 vim createTable.sh #!/bin/sh str="CA6430M,H ...
- CSS-JQUERY笔记
Ready $(document).ready(function(){ }) Input_div_span Input-长度限制 <input maxLength="2"&g ...
- 数据人员Sql必会——行转列
今天被问到列转行的问题,竟然没有回答上来,回想自己也是数据开发人员,平时的积累真是不到位,下面总结一下列转行. 假设咱们有一个学生得分数据表:student_score CREATE TABLE st ...
- WebDriver兼容SeleniumRC(基于C#)
WebDriver兼容SeleniumRC(基于C#)http://www.automationqa.com/forum.php?mod=viewthread&tid=3535&fro ...
- 关于float /double、string类型的hash函数/hash表实现(转)
#include <ext/hash_map> #include <math.h> #include <stdio.h> using namespace std; ...
- 电商大促准备流程v2
1 概述 对于电商企业而言,每年都会有几次大的促销活动,像双十一.店庆等,对于第一次参加这个活动的新手,难免会有些没有头绪,因而将自己参加双十一.双十二活动中的过程心得进行下总结,一方面供以后工作中继 ...