tags:

views:

883

answers:

3

I have inherited some code that applies a filter to a datagrid, the filter works for but is incredibly slow when there are 500+ rows in the datagrid (it hangs with over 500, works fine with 100 rows), the filter basically says "show me everyone who has paid" or "show me everyone in X country" etc.

The list of rows that match the filter (filteredRows below) is created instantly.

        if (comboBoxFilterCondition.Text == "Contains")
        {
            strSearchFilter += string.IsNullOrEmpty(txtFilterValue.Text) ? " IS NULL" : " LIKE '%" + txtFilterValue.Text + "%'";
        }

        FilterRows(strSearchFilter);

//....

    private void FilterRows(string strSearchFilter)
    {
            DataTable table = dataGridView1.DataSource as DataTable;
            if (table != null)
            {
                List<DataRow> filteredRows = new List<DataRow>(table.Select(strSearchFilter)); //<----Very quick to here

                CurrencyManager cm = (CurrencyManager)BindingContext[dataGridView1.DataSource];
                cm.SuspendBinding();
                foreach (DataGridViewRow row in dataGridView1.Rows)
                {
                    row.Visible = filteredRows.Contains(((DataRowView)row.DataBoundItem).Row); //<---Stuck here
                }
                cm.ResumeBinding(); //<----------Doesn't arrive here

//..... }

Any ideas? Thanks all

A: 

My best guess is that it has something to do with how it compares two DataRow instances. Try replacing

List<DataRow> filteredRows = new List<DataRow>(table.Select(strSearchFilter));

with

HashSet<DataRow> filteredRows = new HashSet<DataRow>(table.Select(strSearchFilter));
AgileJon
Is Hashset in .net 3.5? I'm using .net 2.0. Thanks
Yep, it is a .NET 3.5 thing
AgileJon
+11  A: 

There's no reason to do the filtering yourself. If the datagridview is bound to a DataTable (and it seems that it is), just use the DataTable.DefaultView.RowFilter property. Sample coming...

Ok, I created a simple form with a DataGridView and two buttons. On first button click it applies a datatable, and on the second one it filters it:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DataTable table = new DataTable();
        table.Columns.Add("Name", typeof(string));
        table.Columns.Add("Age", typeof(int));
        table.Rows.Add("John", 25);
        table.Rows.Add("Jack", 34);
        table.Rows.Add("Mike", 17);
        table.Rows.Add("Mark", 54);
        table.Rows.Add("Frank", 37);

        this.dataGridView1.DataSource = table;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var table = this.dataGridView1.DataSource as DataTable;
        table.DefaultView.RowFilter = "Age > 30";

    }

When you click the second button, the grid will get filtered automatically. This should work MUCH quicker than doing it manually yourself. Have a look at that link I showed you earlier, as well as this one: http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx for more info on building out the filter expression.

BFree
I wouldn't recommand using the default view... you might need the hidden data somewhere else, and it won't appear in the default view if you do that. I think it's better to create a new DataView on the table. Otherwise, good answer, I vote it up ;)
Thomas Levesque
+1  A: 

The reason it is probably slow is that each time you call the Contains method it has to loop through all the rows in your filteredRows list until it finds the one you are looking for. This means that you are potentially looping upto 500 times each time through the loop.

BFree has the correct answer on how to solve the problem. However I will add that if you are binding more than one thing to the table you can bind to a BindingSource instead. The binding source has a filter property which you can set.

Caleb Vear