实现虚拟模式的动态数据加载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 ...
随机推荐
- nodejs - 如何完全更新
Nodejs可以毫不犹豫地说一个版本狂魔,时不时就发布一个版本,而且还一直没有一个1.0版本,好囧呀,对于我们这些有强迫症的人来说,的确不是好事. 下面我就说一下Nodejs中常见的更新方式. 1. ...
- 使用Condition实现多线程之间调用(生产消费模式)
一,object 类的wait(),notify()和notifyAll() Java 线程类也是一个object 类,它的实例都继承自java.lang.Thread 或其子类.wait(),not ...
- Eclipse中集成Tomcat
问题: 很多时候在Eclipse中启动Tmocat后,不能访问本机的localhost:8080主页,并且其他项目也不能访问. 原因: 打开Tomcat下的webapp后也找补到项目目录,这是因为Ec ...
- Mac安装win7
为了给老板的mac air装上win7,研究了大致4-5小时,终于搞定! 新版的air安装实在是太不容易了,现在记录如下: 制作硬件:8G优盘一个(至少大于6G空白优盘一个),Macbook ai ...
- FastReport.Net在Rozor中的应用
Webconfig中配置 IIS6.0 <system.web> <httpHandlers> 下增加 <httpHandlers> <add path=&q ...
- VPN有什么用?(转载)
VPN有什么用?转载自:http://www.chinaz.com/web/2012/0320/240819.shtml 当你非常非常想访问一个国外的网站,而这个网站被防火墙屏蔽的时候,你应该怎么做呢 ...
- [原]如何在Android用FFmpeg解码图像
前一篇[原]如何用Android NDK编译FFmpeg 我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库.这篇文章我们就可以使用Android下的JNI来调用FFMpeg进 ...
- shell Builtin variables(shell内建变量)
内容来自:abs-guide $BASH The path to the Bash binary itself bash$ echo $BASH /bin/bash $BASH_ENV An envi ...
- Python 程序如何高效地调试?
作者:Rui L链接:https://www.zhihu.com/question/21572891/answer/26046582来源:知乎著作权归作者所有,转载请联系作者获得授权. 这个要怒答一发 ...
- andriod手机签到应用服务器设计
最近导师要求我和另一个同学开发一个手机上课签到应用,我负责客户端和服务器之间的通信架构编写和数据的存储 本人大学四年只用过汇编和C/C++,因此对andriod开发还是一窍不通,花了一个星期写出来了基 ...