65.9K
CodeProject 正在变化。 阅读更多。
Home

在DataGridView中加载十亿行

2009 年 8 月 4 日

CPOL

2分钟阅读

viewsIcon

66241

downloadIcon

3223

本文介绍如何在不使用其 RowCount(占用大量内存)的情况下,以虚拟方式在 DataGridView 中加载行。

引言

本文介绍如何在 DataGridView 中加载行,而无需使用其 RowCount

DataGridView 的 VirtualMode 机制并没有达到它承诺的效果。每当你设置 RowCount 时,DataGridView 将基于它创建行实例;当将其设置为十万或百万时,行对象实例化时间可能会令人沮丧。

促使我编写这段代码的原因是我创建了自己的查找框,并且我希望使其具有前瞻性,即,即使有成千上万或数十万行数据进行查找,程序也应该能够扩展。在初始代码中,我使用了 DataGridView 的 VirtualMode,但当数据源为十万行时,存在一个问题,这个问题与这篇文章中的另一位程序员共享:http://social.msdn.microsoft.com/Forums/en-US/winformsdatacontrols/thread/243a81e7-909b-4c8e-9d28-6114248cf66e

Using the Code

为了更清晰地划分 UI 和数据访问,我将它们放在单独的区域中。查询结果被分配给模块范围内可见的 _dsDataSet)对象。

_ds = Material_LookupList(Filter, 0);

显示结果的列

SetGridDisplay();

分配屏幕可见的行

AdjustRowCount();

最后,显示结果的值

DisplayValues();

我们程序滚动机制中的核心事件(vsbVScrollBar 的一个实例)

private void vsb_ValueChanged(object sender, EventArgs e)
{
    DisplayValues();
}

这是我们设置 DataGrid 列的方式

void SetGridDisplay()
{
    var ftd = new List<string>();

    grd.Columns.Clear();    
    if (this.FieldsToDisplay != null && this.FieldsToDisplay.Length > 0)
    {
        ftd.AddRange(this.FieldsToDisplay);
    }
    else
    {
        foreach (DataColumn c in _ds.Tables["_lookup_result"].Columns)
        {
            if (c.ColumnName == "_lookup_row_number") continue;
            ftd.Add(c.ColumnName);
        }
    }

    foreach (var s in ftd)
    {
        if (s == this.ValueMember && !ShowValueMember) continue;


        string columnName = "ux" + s.Replace(" ", "");


        foreach (DataGridViewColumn dgc in grd.Columns)
        {
            if (dgc.DataPropertyName == s)
                goto goAlreadyAdded;
        }

        grd.Columns.Add(columnName, HeaderizeColumn(s));
        var c = grd.Columns[columnName];
        c.DataPropertyName = s;
        c.Visible = true;

        goAlreadyAdded: ;
    }
}

以下代码是程序的滚动机制(键盘、鼠标、滚动条)。关键原则是只依赖于一个核心逻辑。在我们的代码中,我们只使用垂直滚动条的 Value。相应地,键盘和鼠标滚轮也会请求滚动机制获取 VScrollbar 的值。

void DisplayValues()
{
    for (int r = 0; r < grd.Rows.Count; ++r)
    {
        int absRow = r + vsb.Value;
        DataRow[] drx = _ds.Tables["_lookup_result"].Select(
            string.Format("_lookup_row_number = {0}", absRow));
        
        if (drx.Length == 0)
        {
            _ds = Material_LookupList(Filter, absRow);
            drx = _ds.Tables["_lookup_result"].Select(
                string.Format("_lookup_row_number = {0}", absRow));
        }

        if (drx.Length == 1)
        {
            foreach (DataGridViewColumn c in grd.Columns)
            {
                grd.Rows[r].Cells[c.Name].Value = drx[0][c.DataPropertyName];
            }
        }
        else
        {
            foreach (DataGridViewColumn c in grd.Columns)
            {
                grd.Rows[r].Cells[c.Name].Value = null;
            }
        }

    }
}

void grd_KeyDown(object sender, KeyEventArgs e)
{
    if (grd.CurrentCell == null) return;

    if (e.KeyCode == Keys.Down)
    {
        if (grd.CurrentCell.RowIndex == grd.Rows.Count - 1)
        {
            if (vsb.Value <= vsb.Maximum - vsb.LargeChange)
            {
                e.Handled = true;
                ++vsb.Value;
            }
        }

    }
    else if (e.KeyCode == Keys.Up)
    {
        if (grd.CurrentCell.RowIndex == 0)
        {
            if (vsb.Value > 0)
            {
                e.Handled = true;
                --vsb.Value;
            }
        }

    }
    else if (e.KeyCode == Keys.PageDown)
    {
        if (grd.CurrentCell.RowIndex == grd.Rows.Count - 1)
        {
            if (!(vsb.Value + vsb.LargeChange > vsb.Maximum))
            {
                int newValue = vsb.Value + vsb.LargeChange;

                if (vsb.Maximum - newValue > vsb.LargeChange)
                {
                    vsb.Value = newValue;
                }
                else
                {
                    vsb.Value = vsb.Maximum - vsb.LargeChange + 1;
                }
            }
        }
    }
    else if (e.KeyCode == Keys.PageUp)
    {
        if (grd.CurrentCell.RowIndex == 0)
        {
            if (!(vsb.Value - vsb.LargeChange < 0))
            {
                vsb.Value -= vsb.LargeChange;
            }
            else
            {
                vsb.Value = 0;
            }
        }
    }
}

void grd_MouseWheel(object sender, MouseEventArgs e)
{
    if (e.Delta < 0)
    {
        long rowCount = (long)_ds.Tables["_lookup_count"].Rows[0]["_count"];

        if (vsb.Value < rowCount - vsb.LargeChange)
            ++vsb.Value;
    }
    else if (e.Delta > 0)
    {
        if (vsb.Value > 0)
            --vsb.Value;
    }
}

关注点

编写代码时遇到的有趣/有趣/令人恼火的事情? 越界错误 :-) 特别是在 Keys.DownKeys.PageDown 上。这促使我稍微回退代码;更优化的代码,但充斥着错误的,在另一个类(loftyGoalForm1)中可用。如果存在一个回退代码无法处理的用例,我会回到绘图板并重新访问 loftyGoalForm1 的逻辑。

已知限制

十亿行,因为 .NET 的 VScrollbar 只能支持 32 位数字。如果我找到一个 64 位垂直滚动条,我会将其集成到我的 MycFramework 的查找控件中。

© . All rights reserved.