码迷,mamicode.com
首页 > Windows程序 > 详细

最佳实践扩展Windows窗体DataGridView控件 .net 4.5 附示例代码

时间:2015-01-19 20:39:22      阅读:413      评论:0      收藏:0      [点我收藏+]

标签:

Windows窗体DataGridView控件性能调优.net 4.5

 

 

在处理大量数据时, DataGridView 控制可以消耗大量的内存开销,除非你仔细地使用它。 在客户有限的内存,你可以避免一些这方面的开销,避免内存成本高的特性。 你也可以管理的部分或全部数据维护和检索任务自己使用虚拟模式为了定制的内存使用情况。

在这一节中

 
最佳实践扩展Windows窗体DataGridView控件

描述如何使用 DataGridView 控制,避免了不必要的内存使用和在处理大量数据时的性能损失。

虚拟模式的Windows窗体DataGridView控件

描述如何使用虚拟模式来补充或替换标准的数据绑定机制。

介绍:在Windows窗体DataGridView控件中实现虚拟模式

描述如何实现事件处理程序几个虚拟方式。 还演示了如何实现行级回滚并为用户编辑提交。

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

描述如何加载数据的需求,这是非常有用的,当你有更多的数据比可用来显示客户端内存可以存储。

 

1.最佳实践扩展Windows窗体DataGridView控件

.net 4.5
 
 

 DataGridView 控制的目的是提供最大的可伸缩性。 如果你需要显示大量数据,您应该遵循的指导方针中描述这个话题避免消耗大量内存或有辱人格的用户界面(UI)的响应能力。 本主题讨论以下问题:

  • 有效使用单元格样式

  • 有效使用快捷菜单

  • 有效地使用自动调整

  • 使用选定的单元格、行和列的集合

  • 使用共享的行

  • 防止行成为非共享

如果你有特殊的性能需求,您可以实现虚拟模式,提供自己的数据管理操作。 有关更多信息,请参见 数据显示模式在Windows窗体DataGridView控件 

有效使用单元格样式

 

每个单元格、行和列可以有它自己的样式信息。 信息存储在风格 DataGridViewCellStyle 对象。 创建单元格样式对象对于许多个人DataGridView 元素可以是低效的,特别是当处理大量的数据。 为了避免影响性能,使用以下指南:

有效使用快捷菜单

 

每个单元格、行和列可以有它自己的快捷菜单。 快捷菜单中 DataGridView 控制用 contextmenustrip 控制。 与单元格样式对象一样,许多个人创建快捷方式菜单 DataGridView 元素将对性能造成负面影响。 为了避免这种惩罚,使用以下指南:

  • 避免创建快捷方式菜单为单个细胞和行。 这包括行模板,克隆及其快捷菜单当新行添加到控制。 最大的可伸缩性,只使用控制的contextmenustrip 财产为整个控制指定一个快捷菜单。

  • 如果你需要多个快捷方式菜单为多个行或细胞,处理 CellContextMenuStripNeeded 或 RowContextMenuStripNeeded 事件。 这些事件让你管理自己的快捷菜单对象,允许您调整性能。

有效地使用自动调整

 

行,列,可以自动调整大小和标题单元格内容更改,这样整个细胞显示没有剪切的内容。 改变分级模式还可以调整行、列和标题。 确定正确的尺寸, DataGridView 控制必须检查每个单元格的值,它必须适应。 在处理大型数据集时,这种分析可能会对性能造成负面影响的控制自动调整发生时。 为了避免性能损失,使用以下指南:

有关更多信息,请参见 在Windows窗体DataGridView控件大小的选择 

使用选定的单元格、行和列的集合

 

的 SelectedCells 不执行有效地选择大集合。 的 SelectedRows 和 SelectedColumns 集合也可以是低效的,虽然从一个较小的程度上,因为有很多行比细胞在一个典型的少 DataGridView 控制,许多列少于行。 为了避免性能损失在处理这些集合时,使用以下指南:

使用共享的行

 

中实现高效的内存使用 DataGridView 控制通过共享行。 行将分享尽可能多的信息关于他们的外观和行为通过共享的实例DataGridViewRow 类。

可以很容易地共享行实例节省内存,行成为非共享。 例如,每当用户直接与细胞相互作用,成为非共享它的行。 因为这无法避免,这一主题的指导方针是有用的只有当处理大量数据,只有当用户将与一个相对较小的数据每次运行你的程序的一部分。

一行不能在一个共享的 DataGridView 如果它的任何细胞包含值控制。 当 DataGridView 控件绑定到一个外部数据源或当你实现虚拟模式,提供自己的数据源,细胞外的值存储控制,而不是在细胞对象,允许行共享。

对象只能共享一行如果可以确定所有的细胞状态的行和列的状态包含细胞。 如果你改变一个细胞的状态,这样就可以不再是推断从国家的行和列,行不能共享。

例如,一行不能共享在下列情况下:

在绑定模式或虚拟模式,您可以为单个细胞提供工具提示和快捷菜单处理 CellToolTipTextNeeded 和 CellContextMenuStripNeeded 事件。

的 DataGridView 只要控制将自动尝试使用共享一排排被添加到 DataGridViewRowCollection 。 使用以下指南以确保行共享:

确定一行是否共享,使用 DataGridViewRowCollection 。 SharedRow 方法检索行对象,然后检查对象的 指数 财产。 总是有一个共享的行 指数 属性值为1。

防止行成为非共享

 

共享行可以成为非共享的代码或用户操作。 为了避免性能影响,你应该避免造成行成为非共享。 在应用程序开发期间,您可以处理的RowUnshared 事件确定当行成为非共享。 这是有用的调试row-sharing问题时。

为了防止行成为非共享,使用以下指南:

2.虚拟模式的Windows窗体DataGridView控件

 

使用虚拟模式,您可以管理之间的交互 DataGridView 控制和自定义数据缓存。 实现虚拟模式,设置 virtualmode 财产 真正的 和处理一个或多个事件中描述的这个话题。 你通常会处理至少 CellValueNeeded 事件,使控制查找数据缓存中的值。

绑定模式和虚拟模式

 

虚拟模式是必要的,只有当你需要补充或更换绑定模式。 在绑定模式下,设置 数据源 产权和控制自动加载数据从指定源和提交用户更改回去。 您可以控制显示哪些列绑定的,和数据源本身通常处理操作,如排序。

补充绑定模式

 

可以补充绑定模式通过显示的列的列。 这是有时被称为“混合模式”,是用于显示诸如计算值或用户界面(UI)控件。

因为未绑定列外的数据源,他们忽视了数据源的排序操作。 因此,当你在混合模式启用排序,您必须在本地缓存管理的数据和实现让虚拟模式DataGridView 控制与之交互。

关于使用虚拟模式的更多信息维护未绑定列中的值,看到的例子 DataGridViewCheckBoxColumn 。 ThreeState 财产和System.Windows.Forms 。 DataGridViewComboBoxColumn 类引用主题。

更换绑定模式

 

如果绑定模式不能满足您的性能需求,您可以通过虚拟方式管理你所有的数据在一个自定义缓存事件处理程序。 例如,您可以使用虚拟模式来实现即时数据加载机制,从网络数据库检索只有尽可能多的数据作为最优性能是必要的。 这个场景是特别有用的在处理大量数据时在一个缓慢的网络连接或与客户端机器的数量有限的内存或者存储空间。

关于使用虚拟模式的更多信息在实时的情况下,看到的 实现虚拟模式的即时数据加载Windows窗体DataGridView控件 

虚拟方式事件

 

如果您的数据是只读的, CellValueNeeded 事件可能是唯一事件需要处理。 额外的虚拟方式事件让你使特定功能用户编辑、添加和删除行,行级的事务。

一些标准 DataGridView 事件(如当用户添加或删除行发生的事件,或当细胞值编辑,解析,验证,或格式化的)是有用的在虚拟模式,。 你也可以处理事件,让你保持值不是通常存储在一个绑定数据源,如细胞提示文本,细胞和行错误文本,细胞和行快捷菜单数据,和行高数据。

为更多的信息关于实现虚拟模式管理读/写数据行级提交范围,明白了 介绍:在Windows窗体DataGridView控件中实现虚拟模式 

为例,实现了虚拟模式具有承诺范围,看到 virtualmode 属性引用主题。

只有当发生以下事件 virtualmode 属性设置为 真正的 

 

事件

描述

CellValueNeeded

控制使用的检索数据缓存的一个细胞值显示。 这个事件只发生在未绑定列细胞。

CellValuePushed

使用的控制提交用户输入一个细胞到数据缓存。 这个事件只发生在未绑定列细胞。

调用 UpdateCellValue 当改变缓存值之外的一个方法 CellValuePushed 事件处理程序,以确保当前值显示在控制和自动分级模式目前在应用任何效果。

NewRowNeeded

使用的控制来表示数据缓存需要一个新行。

RowDirtyStateNeeded

连续使用的控制,以确定任何未提交的更改。

CancelRowEdit

控制用于显示一行应该回归其缓存值。

以下事件是有用的在虚拟模式,但可以不管 virtualmode 属性设置。

 

事件

描述

UserDeletingRow

UserDeletedRow

RowsRemoved

RowsAdded

使用的控制指示当行被删除或添加,让你更新相应数据缓存。

CellFormatting

CellParsing

CellValidating

CellValidated

RowValidating

RowValidated

使用的控制格式的单元格值显示,解析和验证用户输入。

CellToolTipTextNeeded

控制用于检索时细胞提示文本 数据源 属性设置或 virtualmode 属性是 真正的 

细胞只有当显示工具提示 ShowCellToolTips 属性值是 真正的 

CellErrorTextNeeded

RowErrorTextNeeded

控制检索单元格或行错误使用的文本的时候 数据源 属性设置或 virtualmode 属性是 真正的 

调用 UpdateCellErrorText 法或者 UpdateRowErrorText 方法当你改变细胞或行错误文本,以确保当前值显示在控制。

细胞和行错误符号时显示 ShowCellErrors 和 ShowRowErrors 属性值是 真正的 

CellContextMenuStripNeeded

RowContextMenuStripNeeded

使用的控制来检索一个细胞或行 contextmenustrip 当控制 数据源 属性设置或 virtualmode 属性是真正的 

RowHeightInfoNeeded

RowHeightInfoPushed

使用的控制来检索或存储行高信息在数据缓存。 调用 UpdateRowHeightInfo 方法当改变缓存行之外的高度信息 RowHeightInfoPushed 事件处理程序,以确保当前值的显示控制。

最佳实践在虚拟模式

 

如果你是实现虚拟模式,以有效地处理大量的数据,你也想确保高效的工作 DataGridView 控制自己。 更多信息的有效利用细胞风格,自动分级,选择,和行共享,明白了 最佳实践扩展Windows窗体DataGridView控件 

 

 

 

3.介绍:在Windows窗体DataGridView控件中实现虚拟模式

当你想要显示非常大量的表格数据 DataGridView 控制,您可以设置 virtualmode 财产 真正的 和显式地管理控制的交互数据存储。 这允许您调整控制在这种情况下的性能。

的 DataGridView 控制提供了一些事件,你可以处理与自定义数据存储交互。 这个介绍将指导您完成这些事件处理程序实现的过程。 这个话题中的代码示例使用一个非常简单的数据源出于演示目的。 在生产环境中,您通常会只行你需要显示加载到缓存中,并处理 DataGridView 事件进行交互和更新缓存。 有关更多信息,请参见 实现虚拟模式的即时数据加载Windows窗体DataGridView控件

复制这个主题作为一个清单中的代码,看看 如何:实现虚拟模式在Windows窗体DataGridView控件 

创建表单

 

实现虚拟模式

  1. 创建一个类,来源于 形式 并包含一个 DataGridView 控制。

    下面的代码包含了一些基本的初始化。 它声明一些变量,将在后面使用步骤,提供了一个 主要 方法,并提供了一个简单的表单布局在类的构造函数。

技术分享
using System;
using System.Windows.Forms;

public class Form1 : Form
{
    private DataGridView dataGridView1 = new DataGridView();

    // Declare an ArrayList to serve as the data store.  
    private System.Collections.ArrayList customers =
        new System.Collections.ArrayList();

    // Declare a Customer object to store data for a row being edited. 
    private Customer customerInEdit;

    // Declare a variable to store the index of a row being edited.  
    // A value of -1 indicates that there is no row currently in edit.  
    private int rowInEdit = -1;

    // Declare a variable to indicate the commit scope.  
    // Set this value to false to use cell-level commit scope.  
    private bool rowScopeCommit = true;

    [STAThreadAttribute()]
    public static void Main()
    {
        Application.Run(new Form1());
    }

    public Form1()
    {
        // Initialize the form. 
        this.dataGridView1.Dock = DockStyle.Fill;
        this.Controls.Add(this.dataGridView1);
        this.Load += new EventHandler(Form1_Load);
        this.Text = "DataGridView virtual-mode demo (row-level commit scope)";
    }


...


}
View Code

 

2. 实现一个表单的处理程序 负载 事件,初始化 DataGridView 控制和填充数据存储与样本值。

技术分享
private void Form1_Load(object sender, EventArgs e)
{
    // Enable virtual mode. 
    this.dataGridView1.VirtualMode = true;

    // Connect the virtual-mode events to event handlers.  
    this.dataGridView1.CellValueNeeded += new
        DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);
    this.dataGridView1.CellValuePushed += new
        DataGridViewCellValueEventHandler(dataGridView1_CellValuePushed);
    this.dataGridView1.NewRowNeeded += new
        DataGridViewRowEventHandler(dataGridView1_NewRowNeeded);
    this.dataGridView1.RowValidated += new
        DataGridViewCellEventHandler(dataGridView1_RowValidated);
    this.dataGridView1.RowDirtyStateNeeded += new
        QuestionEventHandler(dataGridView1_RowDirtyStateNeeded);
    this.dataGridView1.CancelRowEdit += new
        QuestionEventHandler(dataGridView1_CancelRowEdit);
    this.dataGridView1.UserDeletingRow += new
        DataGridViewRowCancelEventHandler(dataGridView1_UserDeletingRow);

    // Add columns to the DataGridView.
    DataGridViewTextBoxColumn companyNameColumn = new
        DataGridViewTextBoxColumn();
    companyNameColumn.HeaderText = "Company Name";
    companyNameColumn.Name = "Company Name";
    DataGridViewTextBoxColumn contactNameColumn = new
        DataGridViewTextBoxColumn();
    contactNameColumn.HeaderText = "Contact Name";
    contactNameColumn.Name = "Contact Name";
    this.dataGridView1.Columns.Add(companyNameColumn);
    this.dataGridView1.Columns.Add(contactNameColumn);
    this.dataGridView1.AutoSizeColumnsMode = 
        DataGridViewAutoSizeColumnsMode.AllCells;

    // Add some sample entries to the data store.  
    this.customers.Add(new Customer(
        "Bon app‘", "Laurence Lebihan"));
    this.customers.Add(new Customer(
        "Bottom-Dollar Markets", "Elizabeth Lincoln"));
    this.customers.Add(new Customer(
        "B‘s Beverages", "Victoria Ashworth"));

    // Set the row count, including the row for new records. 
    this.dataGridView1.RowCount = 4;
}
View Code

 

3. 实现一个处理程序 CellValueNeeded 事件,从数据存储中检索请求的细胞值或 客户 当前在编辑对象。

这个事件发生时 DataGridView 控制细胞需要油漆。

技术分享
private void dataGridView1_CellValueNeeded(object sender,
    System.Windows.Forms.DataGridViewCellValueEventArgs e)
{
    // If this is the row for new records, no values are needed. 
    if (e.RowIndex == this.dataGridView1.RowCount - 1) return;

    Customer customerTmp = null;

    // Store a reference to the Customer object for the row being painted. 
    if (e.RowIndex == rowInEdit)
    {
        customerTmp = this.customerInEdit;
    }
    else 
    {
        customerTmp = (Customer)this.customers[e.RowIndex];
    }

    // Set the cell value to paint using the Customer object retrieved. 
    switch (this.dataGridView1.Columns[e.ColumnIndex].Name)
    {
        case "Company Name":
            e.Value = customerTmp.CompanyName;
            break;

        case "Contact Name":
            e.Value = customerTmp.ContactName;
            break;
    }
}
View Code

 

4.实现一个处理程序 CellValuePushed 事件中存储一个编辑单元格值 客户 对象代表行进行编辑。 这个事件发生时只要用户提交一个细胞值变化。

技术分享
private void dataGridView1_CellValuePushed(object sender,
    System.Windows.Forms.DataGridViewCellValueEventArgs e)
{
    Customer customerTmp = null;

    // Store a reference to the Customer object for the row being edited. 
    if (e.RowIndex < this.customers.Count)
    {
        // If the user is editing a new row, create a new Customer object. 
        if (this.customerInEdit == null)
        {
            this.customerInEdit = new Customer(
                ((Customer)this.customers[e.RowIndex]).CompanyName,
                ((Customer)this.customers[e.RowIndex]).ContactName);
        }
        customerTmp = this.customerInEdit;
        this.rowInEdit = e.RowIndex;
    }
    else
    {
        customerTmp = this.customerInEdit;
    }

    // Set the appropriate Customer property to the cell value entered.
    String newValue = e.Value as String;
    switch (this.dataGridView1.Columns[e.ColumnIndex].Name)
    {
        case "Company Name":
            customerTmp.CompanyName = newValue;
            break;

        case "Contact Name":
            customerTmp.ContactName = newValue;
            break;
    }
}
View Code

 

5. 实现一个处理程序 NewRowNeeded 创建一个新的事件 客户 对象代表一个新创建的行。

这个事件发生时用户输入新记录的行。

技术分享
private void dataGridView1_NewRowNeeded(object sender,
    System.Windows.Forms.DataGridViewRowEventArgs e)
{
    // Create a new Customer object when the user edits 
    // the row for new records. 
    this.customerInEdit = new Customer();
    this.rowInEdit = this.dataGridView1.Rows.Count - 1;
}
View Code

 

6. 实现一个处理程序 RowValidated 事件,保存新的或修改的行数据存储。

这个事件发生时用户更改当前行。

技术分享
private void dataGridView1_RowValidated(object sender,
    System.Windows.Forms.DataGridViewCellEventArgs e)
{
    // Save row changes if any were made and release the edited  
    // Customer object if there is one. 
    if (e.RowIndex >= this.customers.Count &&
        e.RowIndex != this.dataGridView1.Rows.Count - 1)
    {
        // Add the new Customer object to the data store. 
        this.customers.Add(this.customerInEdit);
        this.customerInEdit = null;
        this.rowInEdit = -1;
    }
    else if (this.customerInEdit != null &&
        e.RowIndex < this.customers.Count)
    {
        // Save the modified Customer object in the data store. 
        this.customers[e.RowIndex] = this.customerInEdit;
        this.customerInEdit = null;
        this.rowInEdit = -1;
    }
    else if (this.dataGridView1.ContainsFocus)
    {
        this.customerInEdit = null;
        this.rowInEdit = -1;
    }
}
View Code

 

 

7. 实现一个处理程序 RowDirtyStateNeeded 事件表明是否 CancelRowEdit 事件将发生,当用户信号连续降级在编辑模式下按下ESC两次或一次以外的编辑模式。

默认情况下, CancelRowEdit 发生在连续降级当任何细胞已经被修改,除非在当前行 QuestionEventArgs 。 响应 属性设置为 真正的 在 RowDirtyStateNeeded 事件处理程序。 这个事件非常有用当提交范围是在运行时确定的。

技术分享
private void dataGridView1_RowDirtyStateNeeded(object sender,
    System.Windows.Forms.QuestionEventArgs e)
{
    if (!rowScopeCommit)
    {
        // In cell-level commit scope, indicate whether the value 
        // of the current cell has been modified.
        e.Response = this.dataGridView1.IsCurrentCellDirty;
    }
}
View Code

 

8. 实现一个处理程序 CancelRowEdit 事件,丢弃的值 客户 对象代表当前行。

这个事件发生在用户信号连续降级在编辑模式下按下ESC两次或一次以外的编辑模式。 这个事件不发生如果没有修改当前行细胞或者的价值 QuestionEventArgs 。 响应 属性被设置 假 在一个RowDirtyStateNeeded 事件处理程序。

技术分享
private void dataGridView1_CancelRowEdit(object sender,
    System.Windows.Forms.QuestionEventArgs e)
{
    if (this.rowInEdit == this.dataGridView1.Rows.Count - 2 &&
        this.rowInEdit == this.customers.Count)
    {
        // If the user has canceled the edit of a newly created row,  
        // replace the corresponding Customer object with a new, empty one. 
        this.customerInEdit = new Customer();
    }
    else
    {
        // If the user has canceled the edit of an existing row,  
        // release the corresponding Customer object. 
        this.customerInEdit = null;
        this.rowInEdit = -1;
    }
}
View Code

 

9.

实现一个处理程序 UserDeletingRow 如果删除一个现有的 客户 从数据存储对象或丢弃未保存的 客户 对象代表一个新创建的行。

这事件发生时每当用户点击一行删除一行标题,按删除键。

技术分享
private void dataGridView1_UserDeletingRow(object sender,
    System.Windows.Forms.DataGridViewRowCancelEventArgs e)
{
    if (e.Row.Index < this.customers.Count)
    {
        // If the user has deleted an existing row, remove the  
        // corresponding Customer object from the data store. 
        this.customers.RemoveAt(e.Row.Index);
    }

    if (e.Row.Index == this.rowInEdit)
    {
        // If the user has deleted a newly created row, release 
        // the corresponding Customer object.  
        this.rowInEdit = -1;
        this.customerInEdit = null;
    }
}
View Code

 

10.实现一个简单的 客户 类来代表这段代码示例所使用的数据项。

技术分享
public class Customer
{
    private String companyNameValue;
    private String contactNameValue;

    public Customer()
    {
        // Leave fields empty.
    }

    public Customer(String companyName, String contactName)
    {
        companyNameValue = companyName;
        contactNameValue = contactName;
    }

    public String CompanyName
    {
        get
        {
            return companyNameValue;
        }
        set
        {
            companyNameValue = value;
        }
    }

    public String ContactName
    {
        get
        {
            return contactNameValue;
        }
        set
        {
            contactNameValue = value;
        }
    }
}
View Code

 

测试应用程序

 

您现在可以测试形式,以确保它的行为。

测试表单

  • 编译并运行应用程序。

    您将看到一个 DataGridView 控制填充三个客户记录。 您可以修改多个单元的值并按ESC连续两次在编辑模式以外的编辑模式,一旦恢复整个行其原始值。 当你修改、添加或删除行控制, 客户 对象数据存储的修改、添加或删除。

技术分享

下一个步骤

 

这个应用程序的事件给你一个基本的了解,你必须处理来实现虚拟模式 DataGridView 控制。 你可以改善这个基本的应用程序在许多方面:

  • 实现一个数据存储,从外部数据库缓存值。 缓存应该必要的检索和丢弃的值,以便显示只包含什么是必要的同时消耗少量的内存在客户端计算机。

  • 调整数据存储的性能取决于您的需求。 例如,您可能想要弥补缓慢的网络连接,而不是端计算机内存限制通过使用一个更大的缓存大小和最小化数据库查询的数量。

关于缓存值的更多信息从外部数据库,明白了 如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制 

 

 

 

4.如何:实现虚拟模式的即时数据加载Windows窗体DataGridView控制

以下代码示例展示了如何使用虚拟模式 DataGridView 控制与数据缓存,从服务器加载数据只有当它是必要的。 本例中详细描述 实现虚拟模式的即时数据加载Windows窗体DataGridView控件 

 

技术分享
  1 using System;
  2 using System.Data;
  3 using System.Data.SqlClient;
  4 using System.Drawing;
  5 using System.Windows.Forms;
  6 
  7 public class VirtualJustInTimeDemo : System.Windows.Forms.Form
  8 {
  9     private DataGridView dataGridView1 = new DataGridView();
 10     private Cache memoryCache;
 11 
 12     // Specify a connection string. Replace the given value with a  
 13     // valid connection string for a Northwind SQL Server sample 
 14     // database accessible to your system. 
 15     private string connectionString =
 16         "Initial Catalog=NorthWind;Data Source=localhost;" +
 17         "Integrated Security=SSPI;Persist Security Info=False";
 18     private string table = "Orders";
 19 
 20     protected override void OnLoad(EventArgs e)
 21     {
 22         // Initialize the form. 
 23         this.AutoSize = true;
 24         this.Controls.Add(this.dataGridView1);
 25         this.Text = "DataGridView virtual-mode just-in-time demo";
 26 
 27         // Complete the initialization of the DataGridView. 
 28         this.dataGridView1.Size = new Size(800, 250);
 29         this.dataGridView1.Dock = DockStyle.Fill;
 30         this.dataGridView1.VirtualMode = true;
 31         this.dataGridView1.ReadOnly = true;
 32         this.dataGridView1.AllowUserToAddRows = false;
 33         this.dataGridView1.AllowUserToOrderColumns = false;
 34         this.dataGridView1.SelectionMode =
 35             DataGridViewSelectionMode.FullRowSelect;
 36         this.dataGridView1.CellValueNeeded += new
 37             DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);
 38 
 39         // Create a DataRetriever and use it to create a Cache object 
 40         // and to initialize the DataGridView columns and rows. 
 41         try
 42         {
 43             DataRetriever retriever =
 44                 new DataRetriever(connectionString, table);
 45             memoryCache = new Cache(retriever, 16);
 46             foreach (DataColumn column in retriever.Columns)
 47             {
 48                 dataGridView1.Columns.Add(
 49                     column.ColumnName, column.ColumnName);
 50             }
 51             this.dataGridView1.RowCount = retriever.RowCount;
 52         }
 53         catch (SqlException)
 54         {
 55             MessageBox.Show("Connection could not be established. " +
 56                 "Verify that the connection string is valid.");
 57             Application.Exit();
 58         }
 59 
 60         // Adjust the column widths based on the displayed values. 
 61         this.dataGridView1.AutoResizeColumns(
 62             DataGridViewAutoSizeColumnsMode.DisplayedCells);
 63 
 64         base.OnLoad(e);
 65     }
 66 
 67     private void dataGridView1_CellValueNeeded(object sender,
 68         DataGridViewCellValueEventArgs e)
 69     {
 70         e.Value = memoryCache.RetrieveElement(e.RowIndex, e.ColumnIndex);
 71     }
 72 
 73     [STAThreadAttribute()]
 74     public static void Main()
 75     {
 76         Application.Run(new VirtualJustInTimeDemo());
 77     }
 78 
 79 }
 80 
 81 public interface IDataPageRetriever
 82 {
 83     DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
 84 }
 85 
 86 public class DataRetriever : IDataPageRetriever
 87 {
 88     private string tableName;
 89     private SqlCommand command;
 90 
 91     public DataRetriever(string connectionString, string tableName)
 92     {
 93         SqlConnection connection = new SqlConnection(connectionString);
 94         connection.Open();
 95         command = connection.CreateCommand();
 96         this.tableName = tableName;
 97     }
 98 
 99     private int rowCountValue = -1;
100 
101     public int RowCount
102     {
103         get
104         {
105             // Return the existing value if it has already been determined. 
106             if (rowCountValue != -1)
107             {
108                 return rowCountValue;
109             }
110 
111             // Retrieve the row count from the database.
112             command.CommandText = "SELECT COUNT(*) FROM " + tableName;
113             rowCountValue = (int)command.ExecuteScalar();
114             return rowCountValue;
115         }
116     }
117 
118     private DataColumnCollection columnsValue;
119 
120     public DataColumnCollection Columns
121     {
122         get
123         {
124             // Return the existing value if it has already been determined. 
125             if (columnsValue != null)
126             {
127                 return columnsValue;
128             }
129 
130             // Retrieve the column information from the database.
131             command.CommandText = "SELECT * FROM " + tableName;
132             SqlDataAdapter adapter = new SqlDataAdapter();
133             adapter.SelectCommand = command;
134             DataTable table = new DataTable();
135             table.Locale = System.Globalization.CultureInfo.InvariantCulture;
136             adapter.FillSchema(table, SchemaType.Source);
137             columnsValue = table.Columns;
138             return columnsValue;
139         }
140     }
141 
142     private string commaSeparatedListOfColumnNamesValue = null;
143 
144     private string CommaSeparatedListOfColumnNames
145     {
146         get
147         {
148             // Return the existing value if it has already been determined. 
149             if (commaSeparatedListOfColumnNamesValue != null)
150             {
151                 return commaSeparatedListOfColumnNamesValue;
152             }
153 
154             // Store a list of column names for use in the 
155             // SupplyPageOfData method.
156             System.Text.StringBuilder commaSeparatedColumnNames =
157                 new System.Text.StringBuilder();
158             bool firstColumn = true;
159             foreach (DataColumn column in Columns)
160             {
161                 if (!firstColumn)
162                 {
163                     commaSeparatedColumnNames.Append(", ");
164                 }
165                 commaSeparatedColumnNames.Append(column.ColumnName);
166                 firstColumn = false;
167             }
168 
169             commaSeparatedListOfColumnNamesValue =
170                 commaSeparatedColumnNames.ToString();
171             return commaSeparatedListOfColumnNamesValue;
172         }
173     }
174 
175     // Declare variables to be reused by the SupplyPageOfData method. 
176     private string columnToSortBy;
177     private SqlDataAdapter adapter = new SqlDataAdapter();
178 
179     public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
180     {
181         // Store the name of the ID column. This column must contain unique  
182         // values so the SQL below will work properly. 
183         if (columnToSortBy == null)
184         {
185             columnToSortBy = this.Columns[0].ColumnName;
186         }
187 
188         if (!this.Columns[columnToSortBy].Unique)
189         {
190             throw new InvalidOperationException(String.Format(
191                 "Column {0} must contain unique values.", columnToSortBy));
192         }
193 
194         // Retrieve the specified number of rows from the database, starting 
195         // with the row specified by the lowerPageBoundary parameter.
196         command.CommandText = "Select Top " + rowsPerPage + " " +
197             CommaSeparatedListOfColumnNames + " From " + tableName +
198             " WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
199             lowerPageBoundary + " " + columnToSortBy + " From " +
200             tableName + " Order By " + columnToSortBy +
201             ") Order By " + columnToSortBy;
202         adapter.SelectCommand = command;
203 
204         DataTable table = new DataTable();
205         table.Locale = System.Globalization.CultureInfo.InvariantCulture;
206         adapter.Fill(table);
207         return table;
208     }
209 
210 }
211 
212 public class Cache
213 {
214     private static int RowsPerPage;
215 
216     // Represents one page of data.   
217     public struct DataPage
218     {
219         public DataTable table;
220         private int lowestIndexValue;
221         private int highestIndexValue;
222 
223         public DataPage(DataTable table, int rowIndex)
224         {
225             this.table = table;
226             lowestIndexValue = MapToLowerBoundary(rowIndex);
227             highestIndexValue = MapToUpperBoundary(rowIndex);
228             System.Diagnostics.Debug.Assert(lowestIndexValue >= 0);
229             System.Diagnostics.Debug.Assert(highestIndexValue >= 0);
230         }
231 
232         public int LowestIndex
233         {
234             get
235             {
236                 return lowestIndexValue;
237             }
238         }
239 
240         public int HighestIndex
241         {
242             get
243             {
244                 return highestIndexValue;
245             }
246         }
247 
248         public static int MapToLowerBoundary(int rowIndex)
249         {
250             // Return the lowest index of a page containing the given index. 
251             return (rowIndex / RowsPerPage) * RowsPerPage;
252         }
253 
254         private static int MapToUpperBoundary(int rowIndex)
255         {
256             // Return the highest index of a page containing the given index. 
257             return MapToLowerBoundary(rowIndex) + RowsPerPage - 1;
258         }
259     }
260 
261     private DataPage[] cachePages;
262     private IDataPageRetriever dataSupply;
263 
264     public Cache(IDataPageRetriever dataSupplier, int rowsPerPage)
265     {
266         dataSupply = dataSupplier;
267         Cache.RowsPerPage = rowsPerPage;
268         LoadFirstTwoPages();
269     }
270 
271     // Sets the value of the element parameter if the value is in the cache. 
272     private bool IfPageCached_ThenSetElement(int rowIndex,
273         int columnIndex, ref string element)
274     {
275         if (IsRowCachedInPage(0, rowIndex))
276         {
277             element = cachePages[0].table
278                 .Rows[rowIndex % RowsPerPage][columnIndex].ToString();
279             return true;
280         }
281         else if (IsRowCachedInPage(1, rowIndex))
282         {
283             element = cachePages[1].table
284                 .Rows[rowIndex % RowsPerPage][columnIndex].ToString();
285             return true;
286         }
287 
288         return false;
289     }
290 
291     public string RetrieveElement(int rowIndex, int columnIndex)
292     {
293         string element = null;
294 
295         if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
296         {
297             return element;
298         }
299         else
300         {
301             return RetrieveData_CacheIt_ThenReturnElement(
302                 rowIndex, columnIndex);
303         }
304     }
305 
306     private void LoadFirstTwoPages()
307     {
308         cachePages = new DataPage[]{
309             new DataPage(dataSupply.SupplyPageOfData(
310                 DataPage.MapToLowerBoundary(0), RowsPerPage), 0), 
311             new DataPage(dataSupply.SupplyPageOfData(
312                 DataPage.MapToLowerBoundary(RowsPerPage), 
313                 RowsPerPage), RowsPerPage)};
314     }
315 
316     private string RetrieveData_CacheIt_ThenReturnElement(
317         int rowIndex, int columnIndex)
318     {
319         // Retrieve a page worth of data containing the requested value.
320         DataTable table = dataSupply.SupplyPageOfData(
321             DataPage.MapToLowerBoundary(rowIndex), RowsPerPage);
322 
323         // Replace the cached page furthest from the requested cell 
324         // with a new page containing the newly retrieved data.
325         cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex);
326 
327         return RetrieveElement(rowIndex, columnIndex);
328     }
329 
330     // Returns the index of the cached page most distant from the given index 
331     // and therefore least likely to be reused. 
332     private int GetIndexToUnusedPage(int rowIndex)
333     {
334         if (rowIndex > cachePages[0].HighestIndex &&
335             rowIndex > cachePages[1].HighestIndex)
336         {
337             int offsetFromPage0 = rowIndex - cachePages[0].HighestIndex;
338             int offsetFromPage1 = rowIndex - cachePages[1].HighestIndex;
339             if (offsetFromPage0 < offsetFromPage1)
340             {
341                 return 1;
342             }
343             return 0;
344         }
345         else
346         {
347             int offsetFromPage0 = cachePages[0].LowestIndex - rowIndex;
348             int offsetFromPage1 = cachePages[1].LowestIndex - rowIndex;
349             if (offsetFromPage0 < offsetFromPage1)
350             {
351                 return 1;
352             }
353             return 0;
354         }
355 
356     }
357 
358     // Returns a value indicating whether the given row index is contained 
359     // in the given DataPage.  
360     private bool IsRowCachedInPage(int pageNumber, int rowIndex)
361     {
362         return rowIndex <= cachePages[pageNumber].HighestIndex &&
363             rowIndex >= cachePages[pageNumber].LowestIndex;
364     }
365 
366 }
VirtualJustInTimeDemo

编译的代码

这个示例需要:
  • 引用到系统,系统。 数据系统。 Xml和System.Windows。 表单组件。

  • 访问服务器与罗斯文SQL server安装的示例数据库。

  • 运行如图

技术分享

从命令行信息建设这个例子对Visual Basic或Visual c#,明白了 建筑从命令行(Visual Basic) 或 命令行构建与csc.exe 。 你也可以在Visual Studio构建这个例子的代码粘贴到一个新项目。 也看到 如何:编译和运行一个完整的Windows窗体代码示例使用Visual Studio 

 

本文地址:http://www.cnblogs.com/endv/p/4234537.html

官方地址:http://msdn.microsoft.com/en-us/library/ms171621.aspx

译者Q:77811970

源代码下载:http://files.cnblogs.com/endv/DataGridViewFrom.rar 点击

最佳实践扩展Windows窗体DataGridView控件 .net 4.5 附示例代码

标签:

原文地址:http://www.cnblogs.com/endv/p/4234537.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!