2012-03-31 15 views
2

私はを、ItemsSourceの私が書いたカスタムコレクションを持つWPFアプリケーションに持っています。コレクションは、すべてのアイテムが特定の要件を満たしていることを強制します(つまり、最小値と最大値の間になければなりません)。特定の新しい項目を追加するためにWPF DataGridに強制する最良の方法は何ですか?

コレクションのクラスシグネチャは次のとおりです。

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

私はの最後に追加された新しいアイテムでDataGrid結果の最後の行の編集をコミットするDataGrid機能を使用できるようにしたいですItemsSource

DataGridはデフォルトのコンストラクタを使用して作成した新しい項目を追加するだけです。だから、(密閉されたクラスであり、そのItemCollection介して)間接的に新しい項目を追加、DataGridは宣言:

TはCheckedObservableCollection内の要素のタイプである
ItemsSource.Add(new T()) 

を。グリッドは代わりに、コレクションに課せられた制約を満たす別のTを追加したいと思います。

私の質問は次のとおりです:これを行う方法がありますか?誰かがすでにこれをしていますか?これを行うには最良の方法(最も簡単でコードに最も速く、パフォーマンスは問題ではありません)ですか?次のように

は現在、私はちょうど私自身で OnExecutedBeginEdit機能を無効にするために DataGridを派生: MinValueは、コレクション内の最小許容アイテムです

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

私はこの解決策が嫌いです。アドバイスがありましたら、私はとても感謝しています!

誰でも興味を持って、私は定期的にDataGridItemsSourceとしての私の派生クラスを使用して、ちょうどBindingList<T>代わりのObservableCollection<T>から導出することにより問題を解決することになったために感謝

+0

は、ここに私の答えを参照していることができhttp://stackoverflow.com/questions/4484256/how- use-a-factory-for-datagrid-canuseraddrows-true/9863828#9863828。 BindingList を使用して、必要な作業を行うことができます。 – Phil

+0

ありがとう! –

答えて

1

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] AddingNewItemは何よりも先に発火します。 EventArgsNewItemプロパティは単にnullです。

イベントのハンドラを提供しても、DataGridはデフォルトのコンストラクタがない場合、 の行を追加できるようにしません。しかし、奇妙なことに(しかし、ありがたいことに)あなたが持っていればAddingNewItemEventArgsNewItemプロパティを設定すると、決して呼び出されません。

これを行う場合は、誰もコンストラクタを呼び出すことがないように、[Obsolete("Error", true)][EditorBrowsable(EditorBrowsableState.Never)]などの属性を使用できます。また、コンストラクタ本体は例外

逆コンパイルを投げるコントロールは、私たちはそこに何が起こっているかを確認することができます...

+0

悲しいことに、私の解決策(受け入れられた答え)は、私が必要としていた唯一の静的型付けされた汎用ソリューションです。イベントの最新のバージョンでは、オブジェクトを介してDataGridアイテムがキャストされ、仮想化のパフォーマンスが低下します。パフォーマンスは問題ではないと言いましたが、私は気が変わりました。 –

関連する問題