【发布时间】:2009-07-23 14:51:55
【问题描述】:
我有一个非常简单的数据库,我正在使用 linq to sql。 我有一个 datagridview 来显示表格的内容。如果可能的话,我希望用户能够过滤出现在 datagridview 中的行,而无需对数据库进行另一次查询(我的资源真的很低,所以解决方案必须尽可能快)。
我想过使用BindingSource类的Filter属性,所以我创建了一个,将DataSource属性设置为linq to sql表达式。当用户添加过滤器时,我设置了 Filter 属性。半小时后,我发现 BindingSource 不支持过滤。地狱,太棒了;但那又如何呢?在使用谷歌又花了半个小时并发现基本上没有什么可用的之后,我决定使用 System.Collections.Generic.List 来存储行,因为我能够过滤它。没关系,但我还需要存储原始列表(以防用户删除过滤器),并且我还需要支持多个过滤器。
所以我有两个列表:一个包含查询结果的所有行,另一个包含满足过滤器条件的行。不过,我没有使用多个过滤器对其进行测试。
这行得通,虽然它不是一个很好的解决方案(至少我觉得它不吸引人),但我能得到的就是这些。我决定编写一个包装类,因为我以后可能需要重新使用这个解决方案。我考虑过创建一个 FilteredList 类(在我用 Google 进行了一些搜索并且没有找到任何现有的实现之后),基于以下理论:
- 我存储了一个包含表中所有行的列表,
- 我将过滤器(Predictate 表达式)存储在 BindingList 中(这样我就可以知道列表是否更改并重新过滤行),
- 我将过滤后的行存储在一个列表中,当源列表或过滤器没有进行任何修改时用作缓存,
- 我保留一个布尔值 (_NeedsRefiltering),表示是否必须对源行应用现有过滤器以重新生成缓存,
- 该类必须实现 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 直接设置为 DataSource,这很好 - 直到我添加了一个过滤器并在收到错误时尝试向下滚动:System.IndexOutOfRangeException:索引 180 没有值。
截图: alt text http://shadow.crysis.hu/dgv_error.png
说实话,我不知道出了什么问题。我尝试调用 DataGridView 的 Invalidate、Update 和 Refresh 方法 - 结果相同。
所以...
- 如何使用 linq to sql 有效过滤 DataGridView 中出现的结果?
- 为什么不能将我的 FilteredList 用作 DataGridView 的数据源?
- 上面的代码有什么问题?
非常感谢您抽出宝贵时间(如果您阅读了所有这些内容)和帮助(提前)!
因此,我尝试遵循 Marc Gravell 的建议,并实现了 System.Collections.IList 接口而不是通用接口。它起作用了,所以我可以将它绑定到 DataGridView 的 DataSource 属性,它显示所有行,但是当我添加一个过滤器并开始向下滚动时(由于某种原因,直到我开始滚动列表才会刷新 - Invalidate()、Refresh() 和 Update() 没有帮助)它开始将那些奇怪的 IndexOutOfRangeException-s 作为 DataError-s。
任何想法如何做这些东西?我不敢相信带有 datagridview 的 linq to sql 如此糟糕(对不起,但这越来越荒谬了)......
【问题讨论】:
标签: c# linq-to-sql datagridview