2012-03-31 4 views
2

У меня есть DataGrid в приложении WPF, которое имеет для своей ItemsSource пользовательскую коллекцию, которую я написал. Коллекция обеспечивает, чтобы все ее элементы удовлетворяли определенному требованию (а именно, они должны быть между минимальными и максимальными значениями).Каков наилучший способ заставить WPF DataGrid добавить определенный новый элемент?

класс Подпись коллекции является:

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 наименьшее допустимое элемент в коллекции.

Мне не нравится это решение. Если у кого-нибудь из вас есть совет, я был бы очень благодарен!

Благодаря

+0

Смотрите мой ответ здесь http://stackoverflow.com/questions/4484256/how- в использовании мини-завод-для-DataGrid-canuseraddrows-истинном/9863828 # 9863828. Вы, вероятно, можете делать то, что вам нужно, используя BindingList . – Phil

+0

Спасибо! Звучит хорошо для меня –

ответ

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) объектов или типов значений.

2

Эта проблема теперь полуразрешима под 4.5 с использованием события AddingNewItemDataGrid. Here is my answer to a similar question.

Я решил проблему, используя событие DataGrid's AddingNewItem. Это почти entirely undocumented event не только сообщает вам, что добавляется новый элемент, но также [позволяет вам выбирать, какой элемент добавляется] [2]. AddingNewItem стреляет прежде всего; имущество NewItemEventArgs - это просто null.

Даже если вы предоставите обработчик для события, DataGrid откажется предоставить пользователю возможность добавлять строки , если класс не имеет конструктора по умолчанию.Однако причудливо (но, к счастью), если у вас его есть, и установите NewItem свойство AddingNewItemEventArgs, он никогда не будет вызван.

Если вы решите сделать это, вы можете использовать такие атрибуты, как [Obsolete("Error", true)] и [EditorBrowsable(EditorBrowsableState.Never)], чтобы убедиться, что никто никогда не вызывает конструктор. Вы также можете иметь тело выдает исключение

декомпиляции управление позволяет нам увидеть, что происходит там ...

+0

К сожалению, мое решение (принятый ответ) по-прежнему остается единственным типичным типичным решением, которое соответствует потребностям, которые у меня были. Событие: самая новая версия WPF бросает элементы DataGrid через объект и разрушает производительность виртуализации. Я знаю, что я сказал, что выступление не имеет значения, но я передумал. –