LINQ to SQL的,在一个DataGridView滤波结果(Linq to sql, filt

2019-06-26 15:06发布

我有我使用LINQ to SQL中的其中非常简单的数据库。 我有一个DataGridView以显示表的内容。 我希望用户能够过滤出现在DataGridView行,如果可能的话未做另一个数据库查询(我是真的资源低,因此该解决方案必须尽可能快)。

我想过使用的BindingSource类中的过滤性能,所以我创建一个DataSource属性设置为LINQ to SQL的表达。 当用户添加了一个过滤器,我设置了Filter属性。 像半一小时后,我发现,这BindingSource的不支持筛选。 该死的,伟大的; 那么是什么呢? 再花半小时使用谷歌和发现基本没什么可用之后,我deceided使用System.Collections.Generic.List存储行,因为我能够筛选。 这是所有的权利,但我也需要存储原始列表(万一用户删除一个过滤器),我还需要支持多个过滤器。

所以,我有两个解释:一个查询结果的所有行和一个与满足过滤器的条件的行。 我没有以多个滤波器测试,虽然。

这工作,虽然它不是一个非常好的解决方案(至少我没有发现它吸引人),但是这是所有我已经得到了。 我deceided写一个包装类,因为我可能需要在以后重新使用该解决方案随时随地。 我认为有关创建FilteredList类(后我做了一些搜索与谷歌,并没有发现任何现有的实现),基于以下原理:

  • 我与存储在表中的所有行的列表,
  • 我保存的过滤器(这是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中似乎并不接受它作为数据源:未见异常抛出,简单地说,似乎没有什么(似乎没有一个空的DataGridView,但什么都没有 - 而不是列,也不是一个空行来添加更多项目)。 好吧,好吧,那是不可思议。 我试图直接设置_FilteredSource作为数据源,这是很好 - 直到我加入了筛选,并试图当我得到的错误向下滚动:System.IndexOutOfRangeException:索引180不具有价值。

截图: 替代文字http://shadow.crysis.hu/dgv_error.png

说实话,我不知道什么是错的。 我曾试着拨打的DataGridView的一个invalidate,更新和刷新方法 - 同样的结果。

所以...

  • 我怎么能高效过滤器使用的LINQ to SQL在DataGridView出现的结果吗?
  • 为什么我不能用我的FilteredList作为DataGridView的数据源?
  • 什么是与上面的代码的问题?

非常感谢您的宝贵时间(如果你读这一切)和帮助(提前)!


所以,我试图遵循什么马克Gravell建议,并实施了System.Collections.IList接口,而不是普通的一个。 它的工作,这样我就可以把它绑定到DataGridView的DataSource属性,它显示的所有行,但我添加了一个过滤器,并开始向下滚动(出于某种原因,列表不会刷新,直到我开始滚动 - 无效(),刷新()和Update()不帮助它),它开始给那些怪异IndexOutOfRangeException-S作为DataError-S。

任何想法如何做到这一点的东西? 我不能相信,LINQ用的datagridview为sql吸这么辛苦(抱歉,但这是越来越ridicolous)...

Answer 1:

一起工作DataGridView ,你需要实现非泛型IList ,而不是通用IList<T>或者更简单,更好的:从继承BindingList<T>它通过提供事情,比如修改通知INotifyPropertyChanged )。 对于LINQ到SQL工作,我有一些Usenet上的信息可能是有用的(假设它仍持有的水-它已经有一段时间)。

重“问题的其余” ......你能更具体?

再过滤LINQ到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()可能就足够了......



文章来源: Linq to sql, filtering results in a datagridview