2009-07-23 3 views
3

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

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

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

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

  • Я хранить список всех строк в таблице,
  • Я храню фильтры (которые являются предсказывающими выражениями) в BindingList (так что я могу узнать, изменился ли список и перефильтровать строки)
  • Я храню отфильтрованные строки в списке, служащем кешем, когда там не являются изменениями в списке источников или фильтрах,
  • Я сохраняю логическое значение (_NeedsRefiltering), имея в виду, имеют ли существующие фильтры hav e для применения к исходным строкам для восстановления кэша,
  • Класс должен реализовать интерфейс IList, поэтому он может служить источником данных для 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, похоже, не воспринимает его как DataSource: ни одно исключение не генерируется, просто ничего не появляется (не появляется пустое представление datagridview, но ничего вообще - не столбцы, ни пустая строка, чтобы добавить больше Предметы). Ну, это странно. Я попытался установить _FilteredSource напрямую в качестве источника данных, и все было нормально - пока я не добавил фильтр и не попытался прокрутить вниз, когда я получу ошибку: System.IndexOutOfRangeException: индекс 180 не имеет значения.

Скриншот: alt text http://shadow.crysis.hu/dgv_error.png

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

Итак ...

  • Как я могу эффективно фильтровать результаты, появляющиеся в DataGridView, используя linq to sql?
  • Почему я не могу использовать свой FilteredList в качестве источника данных для DataGridView?
  • В чем проблема с кодом выше?

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


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

Любые идеи, как это сделать? Я не могу поверить, что LINQ к SQL с DataGridView сосет так сложно (извините, но это становится ridicolous) ...

ответ

2

Для работы с DataGridView, необходимо реализовать необщего IList, а не общий IList<T> (или проще и лучше: наследовать от BindingList<T>, который предоставляет такие вещи, как уведомления об изменениях через INotifyPropertyChanged). Для работы с LINQ-to-SQL у меня есть info on usenet, который может быть полезен (при условии, что он все еще содержит воду - это было некоторое время).

re «остальная часть проблемы» ... можете ли вы быть более конкретным?

Эффективно отфильтровывая LINQ-to-SQL, вы не хотите использовать Predicate<T>; вы хотите использовать Expression<Func<T,bool>>; это позволяет передать это вниз к базе данных с помощью Queryable.Where, то есть (где у вас есть в IQueryable<T> источник) что-то вроде:

IQueryable<T> data = tableSource; 
// then for each filter "expr" 
{ 
    data = data.Where(expr); 
} 

Дать истинное отфильтрованный список очень сложно. Я сделал это для объектов в памяти (я не могу опубликовать код, хотя), но он принимает лот отслеживания объектов и т. Д. Если это вам абсолютно не понадобится, может быть проще сохранить все просто и просто отображать простые snapsnots, отслеживать только добавления/удаления. Для простых снимков достаточно всего ToBindingList() ...

+0

Спасибо, я перепишу свой класс FilteredList, используя это. Любые идеи по остальной проблеме? Итак, как бы вы фильтруете? И что такое IndexOutOfRangeException? – ShdNx

+0

Будет ли обновляться ответ ... –

+0

Я вижу ... поэтому я должен забыть фильтровать извлеченные данные и повторно запрашивать базу данных при установке фильтра. Использование выражения > - хорошая идея, спасибо! Я немного подожду и посмотрю, есть ли у кого-то другой вариант ... если нет, тогда я приму свой ответ. В любом случае, спасибо за ваше время и помощь! – ShdNx

Смежные вопросы