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

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

问题描述

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



我正在寻找一种方法来注册一个知道如何创建DataGrid对象的工厂。我看过DataGrid和ListCollectionView,但都没有看起来支持我的场景。

解决方案

问题: p>

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



症状:



如果我们设置 DataGrid.CanUserAddRows = true ,然后将项目集合绑定到DataGrid,其中项目没有默认构造函数,则DataGrid不会显示新项目行'。



原因:



当项目集合绑定到任何WPF ItemControl时, WPF将集合包裹在:


  1. a BindingList< T> 时,我们/ library / system.windows.data.bindinglistcollectionview.aspx> BindingListCollectionView BindingListCollectionView 实现 IEditableCollectionView 但不实现 IEditableCollectionViewAddNewItem


  2. a ListCollectionView ListCollectionView 实现 IEditableCollectionViewAddNewItem (因此 IEditableCollectionView )。


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

  public bool CanAddNewItem(来自IEditableCollectionView的方法)
{
get
{
if(!this.IsEditingItem)
return!this.SourceList。 IsFixedSize;
else
返回false;
}
}

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

似乎没有一个简单的方法覆盖此行为。



对于选项1)情况好多了。 DataGrid将新项目的创建委托给BindingListView,BindingListView委托给 BindingList BindingList< T> 还检查是否存在默认构造函数,但幸运的是,BindingList 还允许客户端设置AllowNew属性并附加用于提供新项目的事件处理程序。稍后请参阅解决方案,但以下是中的相关代码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< T> 一样。这将给客户端在需要时创建新项目的第一个裂缝。



不幸的是,这不是直接从DataGrid支持,即使在.NET 4.5中。



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



解决方法:




  • 工具在同一个程序集中:使用部分类



这种情况似乎不大可能,但可以想象实体框架创建了其实体类,默认构造函数(不太可能,因为它们不可序列化),那么我们可以简单地创建一个默认构造函数的部分类。问题解决了。




  • 业务对象位于另一个程序集中,未被封装:创建业务对象的超类型。 li>


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



最初似乎是一个好主意,但是第二个想法可能需要更多的工作,因为我们需要将业务层生成的数据复制到我们的超级用户,



我们需要代码如

  class MyBusinessObject:BusinessObject 
{
public MyBusinessObject(BusinessObject bo){...复制属性bo}
public MyBusinessObject(){}
}

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




  • 业务对象在另一个程序集中,并且被封装(或不是):封装业务对象。



这很容易

  class MyBusinessObject 
{
public BusinessObject {get;私人集合}

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

现在我们需要do是使用一些LINQ来在这些对象的列表之间进行投影,然后绑定到DataGrid中的 MyBusinessObject.BusinessObject



解决方案:(hurray found one)




  • 使用 BindingList< T>



如果我们将业务对象的集合包装在一个 BindingList< BusinessObject> 中,然后将DataGrid绑定到这个,使用几行代码,我们的问题被解决了DataGrid将适当地显示一个新的项目行。

  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并覆盖功能。我部分成功地尝试了这个,可能会做更多的努力。


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.

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.

解决方案

The problem:

"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.)

The symptoms:

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'.

The causes:

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

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

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

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.

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

Non-solutions:

  • Support by DataGrid (not available)

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.

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

.NET 4.5 does appear to have a new event 'AddingNewItem' that wasn't available previously, but this only lets you know a new item is being added.

Work arounds:

  • Business object created by a tool in the same assembly: use a partial class

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.

  • Business object is in another assembly, and isn't sealed: create a super-type of the business object.

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.

We would need code like

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

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

  • Business object is in another assembly, and is sealed (or not): encapsulate the business object.

This is much easier

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

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

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.

The solution: (hurray found one)

  • Use BindingList<T>

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

Other solutions

  • implement IEditableCollectionViewAddNewItem on top of an existing collection type. Probably a lot of work.
  • inherit from ListCollectionView and override functionality. I was partially successful trying this, probably can be done with more effort.

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

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