У меня очень простая база данных, для которой я использую 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) ...
Спасибо, я перепишу свой класс FilteredList, используя это. Любые идеи по остальной проблеме? Итак, как бы вы фильтруете? И что такое IndexOutOfRangeException? – ShdNx
Будет ли обновляться ответ ... –
Я вижу ... поэтому я должен забыть фильтровать извлеченные данные и повторно запрашивать базу данных при установке фильтра. Использование выражения> - хорошая идея, спасибо! Я немного подожду и посмотрю, есть ли у кого-то другой вариант ... если нет, тогда я приму свой ответ. В любом случае, спасибо за ваше время и помощь! –
ShdNx