【问题标题】:How to Sort WinForms DataGridView bound to EF EntityCollection<T>如何对绑定到 EF EntityCollection<T> 的 WinForms DataGridView 进行排序
【发布时间】:2011-08-19 16:18:27
【问题描述】:

我正在尝试将 WinForms DataGridView 绑定到来自 EntityFramework4 对象的 EntityCollection&lt;T&gt;。问题是,我不知道如何让它(自动)排序。

我所做的只是将 BindingSource 的 DataSource 属性设置为实体的集合。

MyBindingSource.DataSource = CurrentItem.InvoiceNotes;

我真的希望我可以添加一个简单的配置来让它工作;我真的不想将我的 EF 集合包装在一个新的 BindingList 容器中。

【问题讨论】:

  • EntityCollection&lt;T&gt; 是继承 IListSource 还是 BindingList?如果没有,那么您应该编写一个由 DataSource.Changed 事件调用的 AutoSort 例程。 Googlenet 上有大量可用的排序例程... :)
  • 它支持 IListSource,但不支持 BindingList。既然它实现了 IListSource,我可以做些什么来启用排序吗?

标签: c# winforms entity-framework-4 datagridview entitycollection


【解决方案1】:

要支持排序,源代码需要实现IBindingList 并启用排序。烦人的是,AFAIK 唯一的内置类型是 DataView

不过,一切都没有丢失;您最好的选择是为您的数据创建一个BindingList&lt;T&gt;——或者更确切地说,是互联网上作为示例可用的众多BindingList&lt;T&gt; 子类之一。 BindingList&lt;T&gt; 让你完成了 90% 的工作——它只需要实现大约 3 个 (IIRC) 额外的方法来获得基本的(单列)排序支持。

Dinesh Chandnani 在 2005 年 (http://blogs.msdn.com/b/dchandnani/archive/2005/03.aspx) 写了一系列文章,很好地解释了通过 BindingSource 进行绑定。它是在 EF 之前编写的,但它提供了一些很好的背景信息。这里有一个花絮:

当然你可以直接将DataGridView绑定到DataTable 绕过BindingSource,但是BindingSource有一定的优势:

  • 它公开了对列表排序、过滤列表等的属性,否则这样做会很痛苦。 (即,如果你绑定 直接DataGridView到DataTable然后对你的DataTable进行排序 需要知道 DataTable 是一个 IListSource ,它知道 可以对作为 DataView 和 DataView 的基础列表进行排序, 过滤等)。
  • 如果您必须设置主视图/子视图,那么 BindingSource 可以很好地做到这一点(更多详细信息请参阅我的上一篇文章)
  • 对 DataTable 的更改被隐藏(也在我之前的帖子中)

【讨论】:

  • 确实很烦人。我已经有一个 BindingList 实现,但是这个网格应该支持添加和删除项目,所以我想直接绑定到 EntityCollection。哦,好吧,我只需要向 EntityCollection 和我的 BindingList 添加新项目;我不得不做更糟糕的事情。
  • 我通常创建一个内部 BindingList 可通过公共 getter 访问。注意:我通常不使用可编辑的数据网格。
【解决方案2】:

除了@Marc-Gravell 的答案之外,还有a library that makes it easy to get sortable DGVs for any list,所以您可以使用它并在EF 集合、IQueryables、IEnumerables 等上调用.ToList()。现在的问题是,如果您使用.ToList()和排序,数据绑定仍然有效吗?在我所有的测试中,(令我惊讶的)答案是yes(我在 DGV 和数据之间使用了BindingSource)。

这是来自 LINQPad 的 sn-p 和演示屏幕截图:

// http://www.csharpbydesign.com/2009/07/linqbugging---using-linqpad-for-winforms-testing.html
void Main()
{
    var context = this;
    using (var form = new Form())
    {
        var dgv = new DataGridView();
        var binder = new BindingSource();
        
        // All of the following variations work
//      var efCollection = context.NOS_MDT_PROJECT;
//      var sortableCollection = new BindingListView<NOS_MDT_PROJECT>(
//          efCollection.ToList());
//      var efCollection = context.NOS_MDT_PROJECT.First()
//          .NOS_DEFL_TEST_SECT;
//      var sortableCollection = new BindingListView<NOS_DEFL_TEST_SECT>(
//          efCollection.ToList());
        var efCollection = 
            from p in context.NOS_MDT_PROJECT
            where p.NMP_ID==365
            from s in p.NOS_GPR_TST_SECT_COMN_DATA
            from l in s.NOS_GPR_TST_LOC_DATA
            select l;
        var sortableCollection = new BindingListView<NOS_GPR_TST_LOC_DATA>(
            efCollection.ToList());
        
        binder.DataSource = sortableCollection;
        dgv.DataSource = binder;
        
        dgv.Dock = DockStyle.Fill;
        form.Controls.Add(dgv);
        form.Shown += (o, e) => {
            dgv.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
        };
        form.ShowInTaskbar=true;
        form.ShowDialog();
        if (context.IsDirty()) // Extension method
        {
            if (DialogResult.Yes == MessageBox.Show("Save changes?", "", 
                MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2))
            {
                context.SaveChanges();
            }
        }
    }
}

(编辑:将 DGV 直接绑定到 BindingListView (BLV) 似乎与在 DGV 和 BLV 之间使用 BindingSource 相同,因此您可以只使用 dgv.DataSource = efCollection 并仍然获得完整的数据绑定。 )

我花了很多时间研究这个问题并试图理解why you can't just sort an EF collection out-of-the-box(或任何集合,就此而言)。以下是有关此问题的许多有用参考资料的链接汇编:

一般数据绑定

一般 DGV 排序和数据绑定

EF 特定

主/详细(又名父/子)视图

如果你想要扩展方法.IsDirty(),它在VB中(需要在具有正确Imports语句的模块中):

''' <summary>
''' Determines whether the specified object context has changes from original DB values.
''' </summary>
''' <param name="objectContext">The object context.</param>
''' <returns>
'''   <c>true</c> if the specified object context is dirty; otherwise, <c>false</c>.
''' </returns>
<System.Runtime.CompilerServices.Extension()> _
Public Function IsDirty(ByVal objectContext As ObjectContext) As Boolean
    Return objectContext.ObjectStateManager.GetObjectStateEntries(
            EntityState.Added Or EntityState.Deleted Or EntityState.Modified).Any()
End Function

【讨论】:

  • 只是为了节省其他人的时间,这个库在 2021 年不适用于我。虽然字形出现在 DGV 中,但列排序很糟糕。
【解决方案3】:

谢谢安德鲁·戴维,his blog has many other interesting things.

这里简单地使用了 Vb.net 中的 BindingListView (BLV) 也可以:

Imports Equin.ApplicationFramework

Dim elements As List(Of projectDAL.Document) = db.Document.Where(
    Function(w)w.IdProject = _activeProject.Id).OrderBy(Function(i) i.Description).ToList

Dim mySource As BindingListView(Of projectDAL.Document)
mySource = New BindingListView(Of projectDAL.Document)(elements)

【讨论】:

    猜你喜欢
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 2012-03-30
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    相关资源
    最近更新 更多