如何为 DataGrid.CanUserAddRows = true 使用工厂 [英] How to use a factory for DataGrid.CanUserAddRows = true

查看:18
本文介绍了如何为 DataGrid.CanUserAddRows = true 使用工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 DataGrid.CanUserAddRows = true 功能.不幸的是,它似乎只适用于具有默认构造函数的具体类.我的业务对象集合不提供默认构造函数.

I would like to use the DataGrid.CanUserAddRows = true feature. Unfortunately, it seems to work only with concrete classes which have a default constructor. My collection of business objects doesn't provide a default constructor.

我正在寻找一种注册工厂的方法,该工厂知道如何为 DataGrid 创建对象.我查看了 DataGrid 和 ListCollectionView,但它们似乎都不支持我的方案.

I'm looking for a way to register a factory that knows how to create the objects for the DataGrid. I had a look at the DataGrid and the ListCollectionView but none of them seems to support my scenario.

推荐答案

问题:

我正在寻找一种注册工厂的方法,该工厂知道如何为 DataGrid 创建对象".(因为我的业务对象集合不提供默认构造函数.)

"I'm looking for a way to register a factory that knows how to create the objects for the DataGrid". (Because my collection of business objects doesn't provide a default constructor.)

症状:

如果我们设置 DataGrid.CanUserAddRows = true 然后将一组项目绑定到 DataGrid 项目没有默认构造函数,那么 DataGrid 不会显示新项目"行'.

If we set DataGrid.CanUserAddRows = true and then bind a collection of items to the DataGrid where the item doesn't have a default constructor, then the DataGrid doesn't show a 'new item row'.

原因:

当项目集合绑定到任何 WPF ItemControl 时,WPF 将集合包装在其中之一:

When a collection of items is bound to any WPF ItemControl, WPF wraps the collection in either:

  1. a BindingListCollectionView 当被绑定的集合是BindingList.BindingListCollectionView 实现了 IEditableCollectionView 但没有实现 <代码>IEditableCollectionViewAddNewItem.

  1. a BindingListCollectionView when the collection being bound is a BindingList<T>. BindingListCollectionView implements IEditableCollectionView but doesn't implement IEditableCollectionViewAddNewItem.

a ListCollectionView 当被绑定的集合是任何其他集合.ListCollectionView 实现 IEditableCollectionViewAddNewItem(因此 IEditableCollectionView).

a ListCollectionView when the collection being bound is any other collection. ListCollectionView implements IEditableCollectionViewAddNewItem (and hence IEditableCollectionView).

对于选项 2),DataGrid 将新项目的创建委托给 ListCollectionView.ListCollectionView 在内部测试是否存在默认构造函数,如果不存在则禁用 AddNew.这是 ListCollectionView 中使用 DotPeek 的相关代码.

For option 2) the DataGrid delegates creation of new items to the ListCollectionView. ListCollectionView internally tests for the existence of a default constructor and disables AddNew if one doesn't exist. Here's the relevant code from ListCollectionView using DotPeek.

public bool CanAddNewItem (method from IEditableCollectionView)
{
  get
  {
    if (!this.IsEditingItem)
      return !this.SourceList.IsFixedSize;
    else
      return false;
  }
}

bool CanConstructItem
{
  private get
  {
    if (!this._isItemConstructorValid)
      this.EnsureItemConstructor();
    return this._itemConstructor != (ConstructorInfo) null;
  }
}

似乎没有一种简单的方法可以覆盖这种行为.

There doesn't seem to be an easy way to override this behaviour.

对于选项 1),情况要好得多.DataGrid 将新项目的创建委托给 BindingListView,后者又委托给 BindingList.BindingList 还检查​​默认构造函数的存在,但幸运的是 BindingList 还允许客户端设置 AllowNew 属性并附加事件处理程序以提供一个新项目.稍后查看解决方案,但这里是BindingList

For option 1) the situation is a lot better. The DataGrid delegates creation of new items to the BindingListView, which in turn delegates to BindingList. BindingList<T> also checks for the existence of a default constructor, but fortunately BindingList<T> also allows the client to set the AllowNew property and attach an event handler for supplying a new item. See the solution later, but here's the relevant code in BindingList<T>

public bool AllowNew
{
  get
  {
    if (this.userSetAllowNew || this.allowNew)
      return this.allowNew;
    else
      return this.AddingNewHandled;
  }
  set
  {
    bool allowNew = this.AllowNew;
    this.userSetAllowNew = true;
    this.allowNew = value;
    if (allowNew == value)
      return;
    this.FireListChanged(ListChangedType.Reset, -1);
  }
}

非解决方案:

  • DataGrid 支持(不可用)

期望 DataGrid 允许客户端附加回调是合理的,DataGrid 将通过回调请求默认的新项目,就像上面的 BindingList 一样.这将使客户在需要时第一次创建新项目.

It would reasonable to expect the DataGrid to allow the client to attach a callback, through which the DataGrid would request a default new item, just like BindingList<T> above. This would give the client the first crack at creating a new item when one is required.

不幸的是,即使在 .NET 4.5 中,DataGrid 也不直接支持此功能.

Unfortunately this isn't supported directly from the DataGrid, even in .NET 4.5.

.NET 4.5 似乎有一个以前不可用的新事件AddingNewItem",但这只会让您知道正在添加新项目.

变通方法:

  • 由同一程序集中的工具创建的业务对象:使用分部类

这种情况似乎不太可能,但假设实体框架创建了没有默认构造函数的实体类(不太可能,因为它们不能序列化),然后我们可以简单地创建一个带有默认构造函数的分部类.问题解决了.

This scenario seems very unlikely, but imagine that Entity Framework created its entity classes with no default constructor (not likely since they wouldn't be serializable), then we could simply create a partial class with a default constructor. Problem solved.

  • 业务对象在另一个程序集中,并且没有密封:创建业务对象的超类型.

这里我们可以继承业务对象类型,并添加一个默认构造函数.

Here we can inherit from the business object type and add a default constructor.

这最初看起来是个好主意,但转念一想,这可能需要做比必要更多的工作,因为我们需要将业务层生成的数据复制到业务对象的超类型版本中.

This initially seemed like a good idea, but on second thoughts this may require more work than is necessary because we need to copy data generated by the business layer into our super-type version of the business object.

我们需要像

class MyBusinessObject : BusinessObject
{
    public MyBusinessObject(BusinessObject bo){ ... copy properties of bo }
    public MyBusinessObject(){}
}

然后是一些 LINQ 以在这些对象的列表之间进行投影.

And then some LINQ to project between lists of these objects.

  • 业务对象在另一个程序集中,并且被密封(或未密封):封装业务对象.

这更容易

class MyBusinessObject
{
    public BusinessObject{ get; private set; }

    public MyBusinessObject(BusinessObject bo){ BusinessObject = bo;  }
    public MyBusinessObject(){}
}

现在我们需要做的就是使用一些 LINQ 在这些对象的列表之间进行投影,然后绑定到 DataGrid 中的 MyBusinessObject.BusinessObject.不需要杂乱的属性包装或复制值.

Now all we need to do is use some LINQ to project between lists of these objects, and then bind to MyBusinessObject.BusinessObject in the DataGrid. No messy wrapping of properties or copying of values required.

解决办法:(赶紧找到一个)

  • 使用BindingList

如果我们将业务对象集合包装在 BindingList 中,然后将 DataGrid 绑定到此,只需几行代码,我们的问题就解决了,DataGrid 将适当地显示一个新的项目行.

If we wrap our collection of business objects in a BindingList<BusinessObject> and then bind the DataGrid to this, with a few lines of code our problem is solved and the DataGrid will appropriately show a new item row.

public void BindData()
{
   var list = new BindingList<BusinessObject>( GetBusinessObjects() );
   list.AllowNew = true;
   list.AddingNew += (sender, e) => 
       {e.NewObject = new BusinessObject(... some default params ...);};
}

其他解决方案

  • 在现有集合类型之上实现 IEditableCollectionViewAddNewItem.可能有很多工作要做.
  • 从 ListCollectionView 继承并覆盖功能.我在尝试这个方面取得了部分成功,可能可以通过更多的努力来完成.

这篇关于如何为 DataGrid.CanUserAddRows = true 使用工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆