强制WPF DataGrid添加特定新项目的最佳方法是什么?

时间:2012-03-31 00:01:19

标签: wpf datagrid datacontract itemssource type-constraints

我在WPF应用程序中有一个DataGrid,它为我的ItemsSource编写了一个自定义集合。该集合强制其所有项目满足特定要求(即它们必须介于某些最小值和最大值之间)。

集合的类签名是:

   public class CheckedObservableCollection<T> : IList<T>, ICollection<T>, IList, ICollection,
                                            INotifyCollectionChanged
                                             where T : IComparable<T>, IEditableObject, ICloneable, INotifyPropertyChanged

我希望能够使用DataGrid功能,在DataGrid的最后一行提交修改会导致新项目添加到ItemsSource的末尾。

不幸的是DataGrid只是添加了一个使用默认构造函数创建的新项目。因此,在添加新项目时,DataGrid间接(通过其ItemCollection密封类)声明:

ItemsSource.Add(new T())

其中T是CheckedObservableCollection中元素的类型。我想为网格添加一个不同的T,一个满足强加于集合的约束。

我的问题是:有没有内置的方法来做到这一点?有人这样做了吗?什么是最好的(最简单,最快的代码;性能不是问题)这样做的方法?

目前我只是派生DataGrid来覆盖我自己的OnExecutedBeginEdit函数,如下所示:

public class CheckedDataGrid<T> : DataGrid where T : IEditableObject, IComparable<T>, INotifyPropertyChanged, ICloneable
{
  public CheckedDataGrid() : base() { }

  private IEditableCollectionView EditableItems {
     get { return (IEditableCollectionView)Items; }
  }

  protected override void OnExecutedBeginEdit(ExecutedRoutedEventArgs e) {
     try {
        base.OnExecutedBeginEdit(e);
     } catch (ArgumentException) {
        var source = ItemsSource as CheckedObservableCollection<T>;
        source.Add((T)source.MinValue.Clone());
        this.Focus();
     }
  }
}

其中MinValue是集合中允许的最小项目。

我不喜欢这个解决方案。如果你们有任何建议我会非常感激!

由于

2 个答案:

答案 0 :(得分:3)

此问题现在使用4.5的{​​{1}}事件在AddingNewItem下可以半解决。 Here is my answer to a similar question

  

我使用DataGrid的DataGrid事件解决了这个问题。这几乎entirely undocumented event不仅会告诉您正在添加新项目,而且[允许您选择要添加的项目] [2]。 AddingNewItem先发火花; AddingNewItem的{​​{1}}属性只是NewItem

     

即使您为事件提供了处理程序,DataGrid也会拒绝允许用户添加   如果类没有默认构造函数,则为行。但是,奇怪的是(但幸运的是)如果你有一个,并设置EventArgs的{​​{1}}属性,它永远不会被调用。

     

如果您选择这样做,您可以使用nullNewItem等属性,以确保没有人调用构造函数。您也可以让构造函数体抛出异常

     

反编译控件让我们看看那里发生了什么......

答案 1 :(得分:1)

对于任何感兴趣的人,我最后只使用BindingList<T>而不是ObservableCollection<T>来推导解决问题,将我的派生类用作常规ItemsSource中的DataGrid

   public class CheckedBindingList<T> : BindingList<T>, INotifyPropertyChanged where T : IEditableObject, INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private Predicate<T> _check;
  private DefaultProvider<T> _defaultProvider;

  public CheckedBindingList(Predicate<T> check, DefaultProvider<T> defaultProvider) {
     if (check == null)
        throw new ArgumentNullException("check cannot be null");
     if (defaultProvider != null && !check(defaultProvider()))
        throw new ArgumentException("defaultProvider does not pass the check");

     _check = check;
     _defaultProvider = defaultProvider;
  }

  /// <summary>
  /// Predicate the check item in the list against.
  /// All items in the list must satisfy Check(item) == true
  /// </summary>
  public Predicate<T> Check {
     get { return _check; }

     set {
        if (value != _check) {
           RaiseListChangedEvents = false;

           int i = 0;
           while (i < Items.Count)
              if (!value(Items[i]))
                 ++i;
              else
                 RemoveAt(i);

           RaiseListChangedEvents = true;
           SetProperty(ref _check, value, "Check");

           ResetBindings();
        }
     }
  }

  public DefaultProvider<T> DefaultProvider {
     get { return _defaultProvider; }
     set {
        if (!_check(value()))
           throw new ArgumentException("value does not pass the check");
     }
  }

  protected override void OnAddingNew(AddingNewEventArgs e) {
     if (e.NewObject != null)
        if (!_check((T)e.NewObject)) {
           if (_defaultProvider != null)
              e.NewObject = _defaultProvider();
           else
              e.NewObject = default(T);
        }

     base.OnAddingNew(e);
  }

  protected override void OnListChanged(ListChangedEventArgs e) {
     switch (e.ListChangedType) {
        case (ListChangedType.ItemAdded):
           if (!_check(Items[e.NewIndex])) {
              RaiseListChangedEvents = false;
              RemoveItem(e.NewIndex);
              if (_defaultProvider != null)
                 InsertItem(e.NewIndex, _defaultProvider());
              else
                 InsertItem(e.NewIndex, default(T));
              RaiseListChangedEvents = true;
           }
           break;
        case (ListChangedType.ItemChanged):
           if (e.NewIndex >= 0 && e.NewIndex < Items.Count) {
              if (!_check(Items[e.NewIndex])) {
                 Items[e.NewIndex].CancelEdit();
                 throw new ArgumentException("item did not pass the check");
              }
           }
           break;
        default:
           break;
     }

     base.OnListChanged(e);
  }

  protected void SetProperty<K>(ref K field, K value, string name) {
     if (!EqualityComparer<K>.Default.Equals(field, value)) {
        field = value;
        if (PropertyChanged != null)
           PropertyChanged(this, new PropertyChangedEventArgs(name));
     }
  }
}

此类不完整,但上面的实现足以验证静态类型(不是由反射或DLR构建)对象或值类型的列表。