Click here to Skip to main content
15,888,610 members
Articles / Desktop Programming / Windows Forms

Load a billion rows in a DataGridView

Rate me:
Please Sign up or sign in to vote.
2.25/5 (8 votes)
4 Aug 2009CPOL2 min read 64.6K   3.2K   29   7
This article presents how to load rows virtually in a DataGridView without using its RowCount (memory hog).

Introduction

This article presents how to load rows in a DataGridView without using its RowCount.

DataGridView's VirtualMode mechanism doesn't deliver what it promises. Whenever you set the RowCount, the DataGridView will create row instances based on it; row object instantiation time can be aggravating when you set it to hundred thousands or millions.

What prompted me to formulate this code is I created my own lookup box, and I sort of wanted to future-proof it, i.e., the program should scale even if you have tens or hundred thousands of rows to look up. On initial code, I used DataGridView's VirtualMode, but there's a problem when the data source is of hundred thousand rows, the same problem shared by another coder from this article.

Using the Code

For clearer delineation between the UI and data access, I put them on separate regions. The query result is assigned on a module-wide visible _ds (DataSet) object.

C#
_ds = Material_LookupList(Filter, 0);

To show the result's columns:

C#
SetGridDisplay();

To allocate screen-visible rows:

C#
AdjustRowCount();

Finally, to display the result's values:

C#
DisplayValues();

The central event in our program scrolling mechanism (vsb is an instance of VScrollBar):

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

This is how we setup the DataGrid's columns:

C#
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: ;
    }
}

The following code is the data scrolling mechanism (keyboard, mouse, scrollbar) of the program. The key principle is to just depend on one central logic. In our code, we just use the vertical scroll bar's Value. Accordingly, the keyboard and mouse wheel also requests the scrolling mechanism for VScrollbar's Value.

C#
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;
    }
}

Points of Interest

Interesting/fun/annoying things while writing the code? Off-by-one error :-) Especially on Keys.Down and Keys.PageDown. This prompted me to scale back the code a little; the more optimize the code, but riddled with errors is available in another class (loftyGoalForm1). If there's a use case that the scaled back code can't handle, I will go back to the drawing board and re-visit loftyGoalForm1's logic.

Known Limitations

Two billion rows, because .NET's VScrollbar can only support 32 bit numbers. If I find a 64 bit vertical scrollbar, I will integrate that in my MycFramework's lookup control.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Philippines Philippines
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 2 Pin
Florian Reischl18-Jul-10 4:47
Florian Reischl18-Jul-10 4:47 
GeneralThe GOTO part can be written as... Pin
musacj12-Aug-09 21:54
musacj12-Aug-09 21:54 
GeneralMy vote of 1 Pin
nørdic4-Aug-09 15:36
nørdic4-Aug-09 15:36 
GeneralRe: My vote of 1 Pin
anagram of iEnableMuch = Michael Buen4-Aug-09 16:15
anagram of iEnableMuch = Michael Buen4-Aug-09 16:15 
GeneralMy vote of 1 Pin
Frank Meffert4-Aug-09 14:12
Frank Meffert4-Aug-09 14:12 
QuestionGOTO? Pin
Rodentor Sano4-Aug-09 13:54
Rodentor Sano4-Aug-09 13:54 
GeneralWow a goto Pin
spoodygoon4-Aug-09 13:17
spoodygoon4-Aug-09 13:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.