如何对绑定到 EF EntityCollection的 WinForms DataGridView 进行排序

发布于 2024-11-05 11:41:45 字数 320 浏览 0 评论 0原文

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

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

MyBindingSource.DataSource = CurrentItem.InvoiceNotes;

我真的希望有一个简单的配置可以添加到其中以使其正常工作;我真的不想将我的 EF Collection 包装在新的 BindingList 容器中。

I'm trying to bind a WinForms DataGridView to an EntityCollection<T> from an EntityFramework4 object. The trouble is, I can't figure out how to get it to sort (automatically).

All I'm doing is setting the BindingSource's DataSource property to the entity's collection.

MyBindingSource.DataSource = CurrentItem.InvoiceNotes;

I really hope there's a simple configuration I can add to this to get it to work; I really don't want to have to wrap my EF Collection in a new BindingList container.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

泪痕残 2024-11-12 11:41:45

为了支持排序,源需要实现启用排序的IBindingList。令人烦恼的是,据我所知,唯一具有此功能的内置类型是 DataView。

不过,一切并没有失去。您最好的选择是创建数据的 BindingList - 或者更确切地说,创建互联网上作为示例的众多 BindingList 子类之一。 BindingList 可以完成 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 可以很好地做到这一点(更多详细信息请参阅我之前的文章)
  • 对数据表的更改被隐藏(也在我的上一篇文章中)

To support sorting, the source needs to implement IBindingList with sorting enabled. Annoyingly, AFAIK the only inbuilt type with this is DataView.

All is not lost, though; your best option is to create a BindingList<T> of your data - or rather, one of the many BindingList<T> subclasses available as examples on the internet. BindingList<T> gets you 90% of the way there - it just needs about 3 (IIRC) additional methods implementing to get basic (one-column) sorting support.

Dinesh Chandnani wrote a series of articles back in 2005 (http://blogs.msdn.com/b/dchandnani/archive/2005/03.aspx) that do a good job of explaining binding via a BindingSource. It was written before EF, but it provides some good background information. Here's one tidbit:

Of course you can bind the DataGridView to the DataTable directly and
bypass the BindingSource, but BindingSource has certain advantages:

  • It exposes properties to Sort the list, Filter the list, etc. which would other wise be a pain to do. (i.e. if you bind the
    DataGridView to the DataTable directly then to Sort the DataTable you
    need to know that DataTable is an IListSource which knows the
    underlying list which is a DataView and a DataView can be sorted,
    filtered, etc.).
  • If you have to set up master/child views then BindingSource does a great job of doing this (more details in my previous post)
  • Changes to the DataTable is hidden (also in my previous post)
许仙没带伞 2024-11-12 11:41:45

除了@Marc-Gravell 的答案之外,还有一个库,可以轻松获取任何列表的可排序 DGV,因此您可以使用它,只需在 EF 集合、IQueryables、IEnumerables 等上调用 .ToList() 即可。现在的问题是,如果您使用.ToList() 和排序,数据绑定仍然有效吗?在我的所有测试中,(令我惊讶的是)答案是(我在 DGV 和数据之间使用BindingSource)。

以下是 LINQPad 的片段和演示的屏幕截图:

Sortable data from EF collection. Sortedscending on scan columns.

// 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 并仍然获得完整的数据绑定。)

我花了很多时间研究这个问题并试图理解为什么不能只对 EF 集合进行排序开箱即用(或任何集合,就此而言)。以下是有关此问题的许多有用参考的链接汇编:

一般数据绑定

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

Adding on to @Marc-Gravell's answer, there is a library that makes it easy to get sortable DGVs for any list, so you can use it and just call .ToList() on EF collections, IQueryables, IEnumerables, etc. Now the question is, if you use .ToList() and sort, will databinding still work? In all of my tests, the (surprising, to me) answer is yes (I use a BindingSource between the DGV and the data).

Here's a snippet from LINQPad and a screenshot to demo:

Sortable data from EF collection. Sorted descending on scan column.

// 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();
            }
        }
    }
}

(EDIT: Binding the DGV directly to the BindingListView (BLV) seems to work the same as using the BindingSource between the DGV and the BLV, so you can just use dgv.DataSource = efCollection and still get full databinding.)

I've spent a lot of time researching this question and trying to understand why you can't just sort an EF collection out-of-the-box (or any collection, for that matter). Here's a compilation of links to a lot of useful references regarding this question:

Data binding in general

DGV sorting and databinding in general

EF specific

Master/Detail (a.k.a. Parent/Child) views

And if you want the extension method .IsDirty(), here it is in VB (needs to be in a Module with the correct Imports statements):

''' <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
几味少女 2024-11-12 11:41:45

谢谢Andrew Davey,他的博客还有很多其他有趣的东西。

这里简单使用BindingListView(BLV) )在 Vb.net 中也可以工作:

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)

Thank you Andrew Davey, his blog has many other interesting things.

Here a simple use of BindingListView (BLV) in Vb.net that works too:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文