2012-03-31 62 views
2

我在一個WPF應用程序中有一個DataGrid,它的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-使用的-A-工廠換數據網格-canuseraddrows真/ 9863828#9863828。你可以用BindingList 來做你所需要的。 – Phil 2012-03-31 00:20:14

+0

謝謝!聽起來不錯 – 2012-04-03 00:31:42

回答

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

現在,使用DataGridAddingNewItem事件在4.5下可以半解決此問題。 Here is my answer to a similar question

我使用DataGrid的AddingNewItem事件解決了這個問題。這幾乎是entirely undocumented event不僅告訴你一個新的項目正在添加,而且[允許讓你選擇哪個項目被添加] [2]。其他任何事情都會發生; EventArgsNewItem財產僅僅是null

即使您爲該事件提供了處理程序,如果該類沒有默認構造函數,DataGrid將拒絕允許用戶添加 行。然而,奇怪的是(但幸好)如果你有一個,並且設置了AddingNewItemEventArgsNewItem屬性,它將永遠不會被調用。

如果您選擇這樣做,您可以使用屬性(如[Obsolete("Error", true)][EditorBrowsable(EditorBrowsableState.Never)])以確保沒有人調用構造函數。您也可以在構造函數體拋出一個異常

反編譯的控制讓我們看看發生了什麼在那裏...

+0

可悲的是,我的解決方案(公認的答案)仍然是唯一符合我所需要的靜態類型的通用解決方案。事件最新版本的WPF通過對象強制轉換DataGrid項目並導致虛擬化性能下降。我知道我說表演並不重要,但我改變了主意。 – 2013-09-13 03:51:48

相關問題