Linq to sql, фильтрация результатов в сетке данных

У меня есть очень простая база данных, для которой я использую linq to sql. У меня есть DataGridView, чтобы показать содержимое таблицы. Я хочу, чтобы пользователь мог фильтровать строки, появляющиеся в сетке данных, если это возможно, без повторного запроса к базе данных (у меня очень мало ресурсов, поэтому решение должно быть максимально быстрым).

Я подумал об использовании свойства Filter класса BindingSource, поэтому создал его, установив для свойства DataSource выражение linq to sql. Когда пользователь добавил фильтр, я установил свойство Filter. Примерно через полчаса я обнаружил, что BindingSource не поддерживает фильтрацию. Черт, отлично; но что тогда? Потратив еще полчаса на использование Google и не найдя практически ничего полезного, я решил использовать System.Collections.Generic.List для хранения строк, потому что я могу это отфильтровать. Это было хорошо, но мне также нужно было сохранить исходный список (в случае, если пользователь удаляет фильтр), и мне также нужно поддерживать несколько фильтров.

Таким образом, у меня было два списка: один со всеми строками, полученными в результате запроса, и один со строками, которые удовлетворяли условиям фильтра. Однако я не тестировал его с несколькими фильтрами.

Это сработало, хотя это не было действительно хорошим решением (по крайней мере, я не нашел его привлекательным), но это было все, что я получил. Я решил написать класс-оболочку, потому что мне может понадобиться повторно использовать это решение в любое время позже. Я думал о создании класса FilteredList (после того, как я сделал некоторые поиски в Google и не нашел каких-либо существующих реализаций), основываясь на следующей теории:

I store a List with all the rows in the table, I store the filters (which are Predictate expressions) in a BindingList (so I can know if the list changed and re-filter the rows), I store the filtered rows in a List, serving as a cache when there are no modifications made on the source list or the filters, I keep a boolean value (_NeedsRefiltering) meaning whether or not the existing filters have to applied on the source rows to regenerate the cache, The class has to implement the IList interface, so it can serve as a DataSource for the DataGridView.

Вот исходный код моего класса 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();
    }
}

У меня были некоторые проблемы из-за двух списков (списка источников и отфильтрованного списка), но я думаю, что я обработал их правильно. Или, может быть, я этого не сделал, потому что DataGridView, похоже, не принимает его как источник данных: исключение не выдается, просто ничего не появляется (не появляется пустое представление данных, но вообще ничего - ни столбцы, ни пустая строка для добавления дополнительных данных). Предметы). Ну, это странно. Я попытался установить _FilteredSource непосредственно как DataSource, и это было нормально - пока я не добавил фильтр и не попытался прокрутить вниз, когда я получаю сообщение об ошибке: System.IndexOutOfRangeException: Index 180 не имеет значения.

Скриншот: альтернативный текст http://shadow.crysis.hu/dgv_error.png

Честно говоря, я понятия не имею, что не так. Я пытался вызвать методы DataGridView Invalidate, Update и Refresh - те же результаты.

Так...

How could I efficiently filter the results appearing in the DataGridView using linq to sql? Why can't I use my FilteredList as a DataSource for the DataGridView? What's the problem with the code above?

Большое спасибо за ваше время (если вы все это прочитали) и помощь (заранее)!

Итак, я попытался следовать советам Марка Гравелла и реализовал интерфейс System.Collections.IList вместо общего. Это сработало, поэтому я мог связать его со свойством DataSource объекта DataGridView и отобразить все строки, но когда я добавил фильтр и начал прокручивать вниз (по какой-то причине список не обновляется, пока я не начну прокручивать - Invalidate (), Refresh () и Update () ему не помогают) он начал выдавать те странные IndexOutOfRangeException-s как DataError-s.

Есть идеи, как это сделать? Я не могу поверить, что linq to sql с datagridview отстой так сильно (извините, но это становится нелепым) ...

Ответы на вопрос(1)

Ваш ответ на вопрос