2017-09-15 91 views
2

我知道有很多关于这个错误的问题(12345等),但我不能找到一个解释的原因这个错误适合我的情况。让我知道如果我错过了!WPF“EditItem”不允许这种观点

首先,我使用自定义类(不是ObservableCollection或任何其他.NET内置的可观察集合)绑定到我的DataGrid ItemsSource。在向你展示它的代码之前,让我解释我是怎么想的(我的假设可能是错的)。

在我看来,为了可以绑定,集合必须实现至少IEnumerableINotifyCollectionChanged。 IEnumerable为了使视图获得要显示的项目(感谢GetEnumerator方法)和INotifyCollectionChanged以便视图知道集合上的更改。

所以我结束了这个类:

public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IEnumerable<TValue>, INotifyCollectionChanged 
{ 
    #region fields 

    private IDictionary<TKey, TValue> _innerDictionary; 

    #endregion 

    #region properties 

    public int Count { get { return _innerDictionary.Count; } } 

    public ICollection<TKey> Keys { get { return _innerDictionary.Keys; } } 

    public ICollection<TValue> Values { get { return _innerDictionary.Values; } } 

    public bool IsReadOnly { get { return false; } } 

    #endregion 

    #region indexors 

    public TValue this[TKey key] 
    { 
     get { return _innerDictionary[key]; } 
     set { this.InternalAdd(new KeyValuePair<TKey, TValue>(key, value)); } 
    } 

    #endregion 

    #region events 

    public event NotifyCollectionChangedEventHandler CollectionChanged; 

    #endregion 

    #region constructors 

    public ObservableDictionary() 
    { 
     _innerDictionary = new Dictionary<TKey, TValue>(); 
    } 

    public ObservableDictionary(int capacity) 
    { 
     _innerDictionary = new Dictionary<TKey, TValue>(capacity); 
    } 

    public ObservableDictionary(IEqualityComparer<TKey> comparer) 
    { 
     _innerDictionary = new Dictionary<TKey, TValue>(comparer); 
    } 

    public ObservableDictionary(IDictionary<TKey, TValue> dictionary) 
    { 
     _innerDictionary = new Dictionary<TKey, TValue>(dictionary); 
    } 

    public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer) 
    { 
     _innerDictionary = new Dictionary<TKey, TValue>(capacity, comparer); 
    } 

    public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) 
    { 
     _innerDictionary = new Dictionary<TKey, TValue>(dictionary, comparer); 
    } 

    #endregion 

    #region public methods 

    public bool ContainsKey(TKey key) 
    { 
     return _innerDictionary.ContainsKey(key); 
    } 

    public bool Contains(KeyValuePair<TKey, TValue> item) 
    { 
     return _innerDictionary.Contains(item); 
    } 

    public void Add(TKey key, TValue value) 
    { 
     this.InternalAdd(new KeyValuePair<TKey, TValue>(key, value)); 
    } 

    public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> items) 
    { 
     if (!items.Any()) 
     { 
      return; 
     } 

     var added = new List<TValue>(); 
     var removed = new List<TValue>(); 

     foreach (var item in items) 
     { 
      TValue value; 
      if (_innerDictionary.TryGetValue(item.Key, out value)) 
      { 
       removed.Add(value); 
      } 

      added.Add(item.Value); 
      _innerDictionary[item.Key] = item.Value; 
     } 

     this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, null)); 

     if (removed.Count > 0) 
     { 
      this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, removed)); 
     } 
    } 

    public void Add(KeyValuePair<TKey, TValue> item) 
    { 
     this.InternalAdd(item); 
    } 

    public bool TryGetValue(TKey key, out TValue value) 
    { 
     return _innerDictionary.TryGetValue(key, out value); 
    } 

    public bool Remove(TKey key) 
    { 
     return this.InternalRemove(key); 
    } 

    public bool Remove(KeyValuePair<TKey, TValue> item) 
    { 
     return this.InternalRemove(item.Key); 
    } 

    public void Clear() 
    { 
     _innerDictionary.Clear(); 
     this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
    } 

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) 
    { 
     _innerDictionary.CopyTo(array, arrayIndex); 
    } 

    public IEnumerator<TValue> GetEnumerator() 
    { 
     return Values.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 

    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() 
    { 
     return _innerDictionary.GetEnumerator(); 
    } 

    #endregion 

    #region private methods 

    /// <summary> 
    /// Adds the specified value to the internal dictionary and indicates whether the element has actually been added. Fires the CollectionChanged event accordingly. 
    /// </summary> 
    /// <param name="key"></param> 
    /// <param name="value"></param> 
    private void InternalAdd(KeyValuePair<TKey, TValue> item) 
    { 
     IList added = new TValue[] { item.Value }; 

     TValue value; 
     if (_innerDictionary.TryGetValue(item.Key, out value)) 
     { 
      this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, new TValue[] { value })); 
     } 

     _innerDictionary[item.Key] = item.Value; 
     this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, null)); 
    } 

    /// <summary> 
    /// Remove the specified key from the internal dictionary and indicates whether the element has actually been removed. Fires the CollectionChanged event accordingly. 
    /// </summary> 
    /// <param name="key"></param> 
    /// <param name="value"></param> 
    private bool InternalRemove(TKey key) 
    { 
     TValue value; 
     if (_innerDictionary.TryGetValue(key, out value)) 
     { 
      _innerDictionary.Remove(key); 
      this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, new TValue[] { value })); 
     } 

     return value != null; 
    } 

    #endregion 
} 

它为了实现隐含的IEnumerable<TValue>.GetEnumerator并明确其他GetEnumerator方法(IDictionaryIEnumerable),以我的观点显示我的字典里只有值,和我映射围绕调用CollectionChanged事件的添加/删除方法。

我的视图模型的定义是这样的:

class MyViewModel 
{ 
    public ObservableDictionary<string, Foo> Foos { get; private set; } 

    public MyViewModel() 
    { 
     this.Foos = new ObservableDictionary<string, Foo>(); 
    } 
} 

并将其绑定到我的看法是这样的:

<DataGrid ItemsSource="{Binding Facts}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" Width="*" /> 
     <DataGridTextColumn Header="Type" Binding="{Binding Type}" IsReadOnly="True" Width="*" /> 
     <DataGridTextColumn Header="Value" Binding="{Binding Value}" IsReadOnly="False" Width="*" /> 
    </DataGrid.Columns> 
</DataGrid> 

然后,当我尝试编辑的价值,我得到指定的错误:

'EditItem' is not allowed for this view

当我在我的代码中放置了一些断点时,我从来没有达到ObservableDictionary indexor setter no r Foo.Value二传手。

我的想法是关于视图如何从绑定集合中获取项目的问题?为什么我得到这个错误和/或我如何授权我的观点为EditItem?如果你希望能够在一个DataGrid编辑数据

+0

[只是一个说明](https://stackoverflow.com/a/4225429/3283203)关于“我永远不会到达ObservableDictionary indexor setter” – Kilazur

回答

2

你的源集合类型(ObservableDictionary<TKey, TValue>)应该实现IList接口。

每当您绑定到WPF中的某个集合属性时,您总是绑定到自动生成的视图,而不是绑定到实际的源集合本身。

运行时为您创建的视图类型取决于源集合的类型,并且源集合必须实现非通用的IList接口,才能使DataGrid控件的内部编辑功能正常工作。

+0

只是'IList'或'IList '?为什么'IEnumerable '还不够?我也试图找到根本原因,而不仅仅是修复,所以你能解释为什么这会起作用吗? – fharreau

+1

非通用IList接口:https://msdn.microsoft。COM/EN-US /库/ system.collections.ilist(V = vs.110)的.aspx。看我的编辑。 – mm8

+0

感谢有关如何绑定工作的精度。所以在我的情况下实现IEnumerable 是没用的? – fharreau