C#Winforms DataGridView在绑定新数据源时,排序列丢失

时间:2015-10-14 18:32:30

标签: c# winforms sorting datagridview

我最初将DGV的数据源设置为SortableBindingList。当我运行程序时,我能够单击任何列标题并按列升序或降序排序。

我已经实现了一个Filter文本框,它使用LINQ过滤DGV中的数据。 用LINQ过滤列表后。我将过滤后的列表重新绑定到DGV,但先前排序的列不再排序。

即使重新绑定新数据源,DGV仍应继续排序吗?

我为此提出的唯一解决方法是将当前的SortedColumn索引和当前的SortOrder存储到变量中,然后在绑定新数据源时重置这些属性。

public void addFragment(Fragment fragmentToBeAdded){
  Fragment topFragment = getCurrentTopFragment();
  //getCurrentTopFragment returns top visible fragment using findFragmentById
    if(!fragmentToBeAdded.getClass().getSimpleName()
     .equals(topFragment.getClass().getSimpleName()){
       //add fragmentToBeAdded
    }
}

更新1 :(新密码)

private void PopulateGrid()
{
     var gridSource = new MySortableBindingList<Case>(_caseList);
     dataGridView_Cases.DataSource = gridSource;
     ConfigureGrid();
}

private void ApplyFilter(string fString)
{
        MySortableBindingList<Case> msbList = new MySortableBindingList<Case>(_caseList.Where(x => (x.StudentLastName.IndexOf(fString, StringComparison.OrdinalIgnoreCase) >= 0) || (x.StudentIDDisplay.ToString().IndexOf(fString, StringComparison.OrdinalIgnoreCase) >= 0)).ToList());
        dataGridView_Cases.DataSource = msbList;                       
}

更新2 :(附加代码)

    private MySortableBindingList<Case> _gridSource = new MySortableBindingList<Case>();
    BindingSource _caseBindingSource = new BindingSource();
    private void PopulateGrid()
    {
        _gridSource = new MySortableBindingList<Case>(_caseList);
        _caseBindingSource.DataSource = _gridSource;
        dataGridView_Cases.DataSource = _caseBindingSource;
        ConfigureGrid();
    }

 private void ApplyFilter(string fString)
 {
     _gridSource.Clear();
     foreach (var fCase in _caseList.Where(x => (x.StudentLastName.IndexOf(fString, StringComparison.OrdinalIgnoreCase) >= 0) || (x.StudentIDDisplay.ToString().IndexOf(fString, StringComparison.OrdinalIgnoreCase) >= 0)).ToList())
       {
          _gridSource.Add(fCase);
       }
      _caseBindingSource.ResetBindings(false);
 }

2 个答案:

答案 0 :(得分:2)

DataGridView不包含自己的排序功能,而是依靠数据源来执行此操作,更具体地说是IBindingList实现。除了排序之外,IBindingList还提供了ListChanged事件,可用于更新附加到其上的任何UI。请注意,IBindingList也是一个列表,即您可以像使用普通列表一样添加/删除/更新项目,因此您不需要创建新列表并将其重新指定为数据源。相反,创建它一次,将UI附加到它,然后在任何时候只更新列表内容,UI将自动反映更改 - 数据绑定之一&#34;魔法&#34;。

现在,为了实现这一切,必须正确实施IBindingListBindingList<T>已经提供了所需的大部分功能,但不幸的是,这不包括排序。您遇到的问题源于您使用有缺陷的第三方组件(通常在没有标准组件时)。

因此,而不是MySortableBindindingList<T>使用以下实现:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
namespace Tests
{
    public class MyBindingList<T> : BindingList<T>
    {
        static readonly Dictionary<string, Func<IEnumerable<T>, IEnumerable<T>>> orderByMethodCache = new Dictionary<string, Func<IEnumerable<T>, IEnumerable<T>>>();
        private static Func<IEnumerable<T>, IEnumerable<T>> GetOrderByMethod(PropertyDescriptor prop, ListSortDirection direction)
        {
            var orderByMethodName = direction == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
            var cacheKey = typeof(T).GUID + prop.Name + orderByMethodName;
            Func<IEnumerable<T>, IEnumerable<T>> orderByMethod;
            if (!orderByMethodCache.TryGetValue(cacheKey, out orderByMethod))
                orderByMethodCache.Add(cacheKey, orderByMethod = CreateOrderByMethod(prop, orderByMethodName));
            return orderByMethod;
        }
        private static Func<IEnumerable<T>, IEnumerable<T>> CreateOrderByMethod(PropertyDescriptor prop, string orderByMethodName)
        {
            var source = Expression.Parameter(typeof(IEnumerable<T>), "source");
            var item = Expression.Parameter(typeof(T), "item");
            var member = Expression.Property(item, prop.Name);
            var selector = Expression.Lambda(member, item);
            var orderByMethod = typeof(Enumerable).GetMethods()
                .Single(a => a.Name == orderByMethodName && a.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(T), member.Type);
            var orderByExpression = Expression.Lambda<Func<IEnumerable<T>, IEnumerable<T>>>(
                Expression.Call(orderByMethod, new Expression[] { source, selector }), source);
            return orderByExpression.Compile();
        }
        List<T> originalList = new List<T>();
        ListSortDirection sortDirection;
        PropertyDescriptor sortProperty;
        bool isSorted;
        bool ignoreListChanged;
        Func<T, bool> filter;
        public MyBindingList() { }
        public MyBindingList(IEnumerable<T> items) { Update(items); }
        protected override bool SupportsSortingCore { get { return true; } }
        protected override PropertyDescriptor SortPropertyCore { get { return sortProperty; } }
        protected override ListSortDirection SortDirectionCore { get { return sortDirection; } }
        protected override bool IsSortedCore { get { return isSorted; } }
        public Func<T, bool> Filter
        {
            get { return filter; }
            set
            {
                filter = value;
                Refresh();
            }
        }
        public void Update(IEnumerable<T> items)
        {
            originalList.Clear();
            originalList.AddRange(items);
            Refresh();
        }
        public void Refresh()
        {
            var items = originalList.AsEnumerable();
            if (Filter != null)
                items = items.Where(filter);
            if (isSorted)
                items = GetOrderByMethod(sortProperty, sortDirection)(items);

            bool raiseListChangedEvents = RaiseListChangedEvents;
            RaiseListChangedEvents = false;
            base.ClearItems();
            foreach (var item in items)
                Add(item);
            RaiseListChangedEvents = raiseListChangedEvents;
            if (!raiseListChangedEvents) return;
            ignoreListChanged = true;
            ResetBindings();
            ignoreListChanged = false;
        }
        protected override void OnListChanged(ListChangedEventArgs e)
        {
            if (!ignoreListChanged)
                originalList = Items.ToList();
            base.OnListChanged(e);
        }
        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            var orderByMethod = GetOrderByMethod(prop, direction);
            sortProperty = prop;
            sortDirection = direction;
            isSorted = true;
            Refresh();
        }
        protected override void RemoveSortCore()
        {
            if (!isSorted) return;
            isSorted = false;
            Refresh();
        }
    }
}

还支持替换内容和用户定义的过滤器。

以下是一个如何使用它的示例,只是为了得到这个想法,但我想您可以轻松地将其映射到您的特定需求:

using System;
using System.Windows.Forms;
namespace Tests
{
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var form = new Form();
            var dg = new DataGridView { Dock = DockStyle.Fill, Parent = form };
            var filterBox = new TextBox { Dock = DockStyle.Bottom, Parent = form };
            var data = new MyBindingList<Person>(new[]
            {
                new Person { FirstName = "Jon", LastName = "Skeet" },
                new Person { FirstName = "Hans", LastName = "Passant" },
                new Person { FirstName = "Ivan", LastName = "Stoev" },
            });
            dg.DataSource = data;
            var filterText = string.Empty;
            filterBox.TextChanged += (sender, e) =>
            {
                var text = filterBox.Text.Trim();
                if (filterText == text) return;
                filterText = text;
                if (!string.IsNullOrEmpty(filterText))
                    data.Filter = person => person.FirstName.Contains(filterText) || person.LastName.Contains(filterText);
                else
                    data.Filter = null;
            };
            Application.Run(form);
        }
    }
}

答案 1 :(得分:0)

您每次都需要重新申请排序,因为您正在应用全新的数据源。

看看你是否可以适应这样的事情:

C# - code against a property using the property name as a string