Linq a sql, filtrando los resultados en un datagridview

Tengo una base de datos muy simple para la que estoy usando linq to sql. Tengo una vista de datos para mostrar los contenidos de la tabla. Quiero que el usuario pueda filtrar las filas que aparecen en la vista de datos, si es posible sin hacer otra consulta a la base de datos (no tengo muchos recursos, por lo que la solución debe ser lo más rápida posible).

Pensé en usar la propiedad Filter de la clase BindingSource, así que creé una, establecí la propiedad DataSource en la expresión linq to sql. Cuando el usuario agregó un filtro, establezco la propiedad Filtro. Después de aproximadamente media hora, descubrí que BindingSource no admite el filtrado. Infierno, genial pero entonces que hace Después de pasar otra media hora usando Google y básicamente no encontré nada utilizable, decidí usar un System.Collections.Generic.List para almacenar las filas, porque puedo filtrar eso. Estaba bien, pero también necesitaba almacenar la Lista original (en caso de que el usuario elimine un filtro) y también debo admitir varios filtros.

Así que tuve dos listas: una con todas las filas que resultó la consulta y otra con las filas que cumplían con las condiciones del filtro. Sin embargo, no lo probé con varios filtros.

Eso funcionó, aunque no era una solución realmente buena (al menos no la encontraba atractiva), pero eso era todo lo que tenía. Decidí escribir una clase de envoltorio, ya que es posible que deba volver a utilizar esta solución en cualquier momento posterior. Pensé en crear una clase FilteredList (después de realizar algunas búsquedas con Google y no encontré ninguna implementación existente), según la siguiente teoría:

Guardo una lista con todas las filas en la tabla,Almaceno los filtros (que son expresiones Predictate) en un BindingList (para que pueda saber si la lista ha cambiado y vuelva a filtrar las filas),Almaceno las filas filtradas en una Lista, que sirve como caché cuando no se realizan modificaciones en la lista de origen o en los filtros,Mantengo un valor booleano (_NeedsRefiltering) que significa si los filtros existentes deben aplicarse en las filas de origen para regenerar el caché,La clase tiene que implementar la interfaz IList, por lo que puede servir como un DataSource para el DataGridView.

Aquí viene el código fuente de mi clase FilteredList:

public class FilteredList<T> : IList<T>
{
    private bool _NeedsReFiltering = false;
    private BindingList<Predicate<T>> _Filters;

    public BindingList<Predicate<T>> Filters
    {
        get
        {
            if (this._Filters == null)
            {
                this._Filters = new BindingList<Predicate<T>>();
                this._Filters.RaiseListChangedEvents = true;
                this._Filters.ListChanged += delegate(object sender, ListChangedEventArgs e)
                {
                    this._NeedsReFiltering = true;
                };
            }
            return this._Filters;
        }
        set
        {
            this._Filters = value;
            this._NeedsReFiltering = true;
        }
    }

    private List<T> _Source;
    public List<T> Source
    {
        get
        {
            return this._Source;
        }
        set
        {
            this._Source = value;
            this._NeedsReFiltering = true;
        }
    }

    private List<T> __FilteredSource = new List<T>();
    private List<T> _FilteredSource
    {
        get
        {
            if (this._NeedsReFiltering)
            {
                this._NeedsReFiltering = false;
                this.Refilter();
            }
            return this.__FilteredSource;
        }
        set
        {
            this.__FilteredSource = value;
        }
    }

    public List<T> FilteredSource // Only for setting it as the DataGridView's DataSource - see my comments after the code
    {
        get
        {
            return this._FilteredSource;
        }
    }

    public FilteredList()
    {
        this._Source = new List<T>();
    }

    public FilteredList(int capacity)
    {
        this._Source = new List<T>(capacity);
    }

    public FilteredList(IEnumerable<T> source)
    {
        this._Source = new List<T>(source);
        this._NeedsReFiltering = true;
    }

    public void Refilter()
    {
        this.__FilteredSource = this._Source;

        if (this._Filters == null)
        {
            return;
        }

        foreach (var filter in this._Filters)
        {
            this.__FilteredSource.RemoveAll(item => !filter(item));
        }
    }

    public int IndexOf(T item)
    {
        return this._FilteredSource.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        this._FilteredSource.Insert(index, item);
        this._Source.Add(item);
    }

    public void RemoveAt(int index)
    {
        //this._Source.RemoveAt(index);
        this._Source.Remove(this.__FilteredSource[index]);
        this._NeedsReFiltering = true;
    }

    public T this[int index]
    {
        get
        {
            return this._FilteredSource[index];
        }
        set
        {
            this._Source[this._Source.FindIndex(item => item.Equals(this._FilteredSource[index]))] = value;
            this._NeedsReFiltering = true;
        }
    }

    public void Add(T item)
    {
        this._Source.Add(item);
        this._NeedsReFiltering = true;
    }

    public void Clear()
    {
        this._Source.Clear();
        this._FilteredSource.Clear();
        this._NeedsReFiltering = false;
    }

    public bool Contains(T item)
    {
        return this._FilteredSource.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        this._FilteredSource.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return this._FilteredSource.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        var r = this._Source.Remove(item);
        this._FilteredSource.Remove(item);
        return r;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return this._FilteredSource.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this._FilteredSource.GetEnumerator();
    }
}

Tuve algunos problemas debido a las dos listas (la lista de fuentes y la lista filtrada), pero creo que las he manejado correctamente. O tal vez no, porque DataGridView no parece aceptarla como fuente de datos: no se lanza ninguna excepción, simplemente no aparece nada (no aparece una vista de datos vacía, pero nada en absoluto, ni las columnas, ni una fila vacía para agregar más) artículos). Bueno, bueno, eso es raro. Intenté configurar _FilteredSource directamente como DataSource, y estuvo bien, hasta que agregué un filtro e intenté desplazarme hacia abajo cuando recibí el error: System.IndexOutOfRangeException: Index 180 no tiene un valor.

Captura de pantalla:texto alternativo http://shadow.crysis.hu/dgv_error.png

Para ser honesto, no tengo idea de lo que está mal. He intentado llamar a los métodos Invalidate, Update y Refresh de DataGridView: los mismos resultados.

Asi que...

¿Cómo podría filtrar eficientemente los resultados que aparecen en el DataGridView usando linq to sql?¿Por qué no puedo usar mi FilteredList como un DataSource para el DataGridView?¿Cuál es el problema con el código de arriba?

¡Muchas gracias por su tiempo (si leyó todo esto) y ayuda (por adelantado)!

Así que traté de seguir lo que Marc Gravell aconsejó e implementé la interfaz System.Collections.IList en lugar de la genérica. Funcionó, por lo que pude vincularlo a la propiedad DataSource de DataGridView, y mostró todas las filas, pero cuando agregué un filtro y comencé a desplazarme hacia abajo (por alguna razón, la lista no se actualiza hasta que comienzo a desplazarme) Invalidate (), Refresh () y Update () no lo ayudan. Comenzó a dar a esas extrañas IndexOutOfRangeException-s como DataError-s.

¿Alguna idea de cómo hacer esto? No puedo creer que linq to sql con datagridview sea tan difícil (lo siento, pero esto se está volviendo ridículo) ...

Respuestas a la pregunta(1)

Su respuesta a la pregunta