实现虚拟模式的动态数据加载Windows窗体DataGridView控件 .net 4.5 (一)

时间:2021-09-09 10:30:12

实现虚拟模式的即时数据加载Windows窗体DataGridView控件

.net 4.5
原文地址 :http://msdn.microsoft.com/en-us/library/ms171624.aspx  译 Q:77811970
 
 实现虚拟模式的原因之一 DataGridView控制只检索数据,因为它是必要的。 这就是所谓的 即时数据加载 。

如果你正在与一个非常大的表在一个远程数据库,例如,您可能希望避免启动延迟,只检索所需的数据显示和检索额外的数据只有当用户新行滚动到视图。 如果客户端电脑运行您的应用程序有一个有限的可用内存来存储数据,您可能还想丢弃未使用的数据从数据库中检索新值。

下面的部分描述了如何使用 DataGridView控制与实时缓存。

复制这个主题作为一个清单中的代码,看看 如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制

表单

 

以下代码示例定义了一个包含一个只读形式 DataGridView与交互的控制 缓存 对象通过一个 CellValueNeeded事件处理程序。 的 缓存 对象管理和使用本地存储的值 DataRetriever 从订单表对象检索值的样本Northwind数据库。 的 DataRetriever 对象,它实现了 IDataPageRetriever 所需的接口 缓存 类,还用于初始化 DataGridView控制行和列。

的 IDataPageRetriever , DataRetriever , 缓存 类型被描述在这个话题。

实现虚拟模式的动态数据加载Windows窗体DataGridView控件 .net 4.5 (一) 请注意

存储敏感信息,如密码,连接字符串内会影响应用程序的安全。 使用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 (一)的更多相关文章

  1. C&num;:将空间数据加载到树视图控件

    自己 整理了 下 代码 测试了下 还行... #region 操作树视图控件 /// <summary> /// 自定义需要的类型 /// </summary> enum Da ...

  2. 【微信小程序】模仿58同城页面制作以及动态数据加载

    完成动态数据的加载,如下 使用上班的空余时间慢慢的学习,相信总有一天我会很熟悉的掌握这门技术. 本次学习小总结: 微信小程序使用的代码基本与HTML.CSS.JS等前段有关知识一样. 微信小程序js使 ...

  3. 在ASP&period;NET中动态加载内容(用户控件和模板)

    在ASP.NET中动态加载内容(用户控件和模板) 要点: 1. 使用Page.ParseControl 2. 使用base.LoadControl 第一部分:加载模板 下 面是一个模板“<tab ...

  4. WPF中动态加载XAML中的控件

    原文:WPF中动态加载XAML中的控件 using System; using System.Collections.Generic; using System.Linq; using System. ...

  5. MVC4加载zTree树小控件

    前言: 第一次学习使用MVC框架,找了个练手项目,加载zTree树小控件.下面我就一步步说明我这次练手的经历以记录.如果有什么错误,希望各位大神帮忙指正,谢谢. 第一步: 利用VS2010新建一个MV ...

  6. 上拉加载下拉刷新控件WaterRefreshLoadMoreView

    上拉加载下拉刷新控件WaterRefreshLoadMoreView 效果: 源码: // // SRSlimeView // @author SR // Modified by JunHan on ...

  7. Echarts通过Ajax实现动态数据加载

    Echarts(3.x版)官网实例的数据都是静态的,实际使用中往往会要求从服务器端取数据进行动态显示,官网教程里给出的异步数据加载很粗略,下面就以官网最简单的实例为例子,详细演示如下过程:1.客户端通 ...

  8. ECharts动态数据加载

    最近有用到ECharts做可视化报表,小结一下 一.准备数据 1.官网下载echarts.min.js 2.引入jquery.js 3.请求用的json数据 { "list":[ ...

  9. ios ableviewcell的动态加载数据,模仿喜马拉雅动态数据加载

    iphone(UITableViewCell)动态加载图片http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Intr ...

随机推荐

  1. CSS3 基于关系的选择器

    常见的基于关系的选择器 选择器 选择的元素 A E 元素A的任一后代元素E (后代节点指A的子节点,子节点的子节点,以此类推) A > E 元素A的任一子元素E(也就是直系后代) E:first ...

  2. peersim中BT网络核心代码解析

    首先大概介绍BT网络运行的整体流程: 开始阶段,一个节点加入到网络中,并向tracker节点发送信息,tracker返回若干个邻居的列表 得到列表后,向每个邻居发送bitfiled信息,来获取他们的文 ...

  3. WPF RichTextBox读取存储文本的方法和常用属性

    1. 取得已被选中的内容: (1)使用 RichTextBox.Document.Selection属性(2)访问RichTextBox.Document.Blocks属性的“blocks”中的Tex ...

  4. hdu 5063 Operation the Sequence(Bestcoder Round &num;13&rpar;

    Operation the Sequence                                                                     Time Limi ...

  5. Unique Paths 解答

    Question A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram be ...

  6. IE 第三方设置cookie失效

    公司的产品,采用多服务分摊压力,中间必须涉及的当然是单点登陆.一般的单点登陆都是通过去用户中心登陆,302或页面回调的方式,返回到登陆前的页面. 公司项目,想用户体验更好些,采用弹框登陆,可以考虑if ...

  7. 压缩OLEVARIANT数据

    TCLIENTDATASET.DATA, TCLIENTDATASET.DELTA, TDATASETPROVIDER.DATA,它们的DATA属性的类型都是OLEVARIANT. 中间层和客户端之间 ...

  8. Collections方法的使用

    public static void main(String[] args) { // 0.给List排序 List<Integer> list = new ArrayList<In ...

  9. 关于表情的战争APP隐私政策网址

    本软件尊重并保护所有使用服务用户的个人隐私权.为了给您提供更准确.更有个性化的服务,本软件会按照本隐私权政策的规定使用和披露您的个人信息.但本软件将以高度的勤勉.审慎义务对待这些信息.除本隐私权政策另 ...

  10. Tomcat8源码笔记&lpar;九&rpar;组件StandardContext启动流程--未完待续

    StandardContext代表的是webapps下项目,一个项目就是一个StandardContext,作为Tomcat组件的一部分,就会实现Lifecycle接口,被Tomcat管理着生命周期, ...