gpt4 book ai didi

C# Winforms DataGridView 绑定(bind)新数据源时排序列丢失

转载 作者:行者123 更新时间:2023-11-30 16:05:46 30 4
gpt4 key购买 nike

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

我已经实现了一个过滤器文本框,它使用 LINQ 过滤 DGV 中的数据。在我使用 LINQ 过滤列表之后。我将筛选后的列表重新绑定(bind)到 DGV,但之前排序的列不再排序。

即使我重新绑定(bind)了一个新的数据源,DGV 是否应该继续排序?

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

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;
}

更新 1:(新代码)

    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:(附加代码)

/// <summary>
/// Source: http://www.codeproject.com/Articles/31418/Implementing-a-Sortable-BindingList-Very-Very-Quic
/// </summary>
/// <typeparam name="T"></typeparam>
public class MySortableBindingList<T> : BindingList<T>
{

// reference to the list provided at the time of instantiation
List<T> originalList;
ListSortDirection sortDirection;
PropertyDescriptor sortProperty;

// function that refereshes the contents
// of the base classes collection of elements
Action<MySortableBindingList<T>, List<T>>
populateBaseList = (a, b) => a.ResetItems(b);

// a cache of functions that perform the sorting
// for a given type, property, and sort direction
static Dictionary<string, Func<List<T>, IEnumerable<T>>>
cachedOrderByExpressions = new Dictionary<string, Func<List<T>,
IEnumerable<T>>>();
/// <summary>
/// Create a sortable binding list
/// </summary>
public MySortableBindingList()
{
originalList = new List<T>();
}

/// <summary>
/// Create a sortable binding list
/// </summary>
public MySortableBindingList(IEnumerable<T> enumerable)
{
originalList = enumerable.ToList();
populateBaseList(this, originalList);
}

/// <summary>
/// Create a sortable binding list
/// </summary>
public MySortableBindingList(List<T> list)
{
originalList = list;
populateBaseList(this, originalList);
}

/// <summary>
/// Look for an appropriate sort method in the cache if not found .
/// Call CreateOrderByMethod to create one.
/// Apply it to the original list.
/// Notify any bound controls that the sort has been applied.
/// </summary>
/// <param name="prop"></param>
/// <param name="direction"></param>
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
/*
Look for an appropriate sort method in the cache if not found .
Call CreateOrderByMethod to create one.
Apply it to the original list.
Notify any bound controls that the sort has been applied.
*/

sortProperty = prop;

var orderByMethodName = sortDirection ==
ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
var cacheKey = typeof(T).GUID + prop.Name + orderByMethodName;

if (!cachedOrderByExpressions.ContainsKey(cacheKey))
{
CreateOrderByMethod(prop, orderByMethodName, cacheKey);
}

ResetItems(cachedOrderByExpressions[cacheKey](originalList).ToList());
ResetBindings();
sortDirection = sortDirection == ListSortDirection.Ascending ?
ListSortDirection.Descending : ListSortDirection.Ascending;
}


private void CreateOrderByMethod(PropertyDescriptor prop,
string orderByMethodName, string cacheKey)
{

/*
Create a generic method implementation for IEnumerable<T>.
Cache it.
*/

var sourceParameter = Expression.Parameter(typeof(List<T>), "source");
var lambdaParameter = Expression.Parameter(typeof(T), "lambdaParameter");
var accesedMember = typeof(T).GetProperty(prop.Name);
var propertySelectorLambda =
Expression.Lambda(Expression.MakeMemberAccess(lambdaParameter,
accesedMember), lambdaParameter);
var orderByMethod = typeof(Enumerable).GetMethods()
.Where(a => a.Name == orderByMethodName &&
a.GetParameters().Length == 2)
.Single()
.MakeGenericMethod(typeof(T), prop.PropertyType);

var orderByExpression = Expression.Lambda<Func<List<T>, IEnumerable<T>>>(
Expression.Call(orderByMethod,
new Expression[] { sourceParameter,
propertySelectorLambda }),
sourceParameter);

cachedOrderByExpressions.Add(cacheKey, orderByExpression.Compile());
}

/// <summary>
/// RemoveSortCore
/// </summary>
protected override void RemoveSortCore()
{
ResetItems(originalList);
}

private void ResetItems(List<T> items)
{

base.ClearItems();

for (int i = 0; i < items.Count; i++)
{
base.InsertItem(i, items[i]);
}
}

/// <summary>
/// SupportsSortingCore
/// </summary>
protected override bool SupportsSortingCore
{
get
{
// indeed we do
return true;
}
}

/// <summary>
/// Ascending or descending
/// </summary>
protected override ListSortDirection SortDirectionCore
{
get
{
return sortDirection;
}
}

/// <summary>
/// A property
/// </summary>
protected override PropertyDescriptor SortPropertyCore
{
get
{
return sortProperty;
}
}

/// <summary>
/// List has changed
/// </summary>
/// <param name="e"></param>
protected override void OnListChanged(ListChangedEventArgs e)
{
originalList = base.Items.ToList();
}
}

最佳答案

DataGridView不包含自己的排序功能,而是依赖数据源来执行此操作,更具体地说是 IBindingList执行。除排序外,IBindingList提供一个 ListChanged可用于更新附加到它的任何 UI 的事件。注意 IBindingList也是一个列表,即您可以像普通列表一样添加/删除/更新项目,因此您不需要创建新列表并将其重新分配为数据源。相反,创建它一次,将 UI 附加到它,然后随时更新列表内容,UI 将自动反射(reflect)更改 - 数据绑定(bind)“魔法”之一。

现在,为了让这一切发生,IBindingList必须正确实现。 BindingList<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);
}
}
}

关于C# Winforms DataGridView 绑定(bind)新数据源时排序列丢失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33132999/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com